// ==UserScript==
// @name         OpenHarmony Insight Review Lite
// @name:zh-CN   OpenHarmony Insight Review Lite
// @namespace    http://tampermonkey.net/
// @version      5.1.4
// @description  Fetches full issue details via GitCode API and sends rich context to an LLM for structured review.
// @description:zh-CN 通过 GitCode API 获取 Issue 完整信息，并将富文本上下文发送给大模型进行结构化审核。
// @author       Raymond
// @match        https://gitcode.com/*/*/pull/*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_registerMenuCommand
// @connect      *
// ==/UserScript==

(function() {
    'use strict';

    // --- 默认配置 ---
    const DEFAULT_TEMPLATE = `
请根据以下Pull Request的变更文件、关联Issue的完整信息，判断该PR是否充分。
关联Issue和文件列表的信息（包括标题和内容体）已通过API获取。

请严格按照以下JSON格式返回你的分析结果，不要在JSON代码块之外添加任何额外的解释或Markdown标记。

{
  "overall_assessment": "从 'Sufficient', 'Needs Improvement', 'Insufficient' 中选择一个",
  "summary_comment": "对结论的1-2句话简短总结",
  "detailed_feedback": [
    {
      "aspect": "标题与描述",
      "rating": "从 '优秀', '良好', '待改进' 中选择一个",
      "comment": "对PR标题和描述是否清晰、提供了足够上下文的评语"
    },
    {
      "aspect": "与Issue的关联性",
      "rating": "从 '优秀', '良好', '待改进' 中选择一个",
      "comment": "基于Issue的完整内容，评估PR的实现是否解决了核心问题"
    },
    {
      "aspect": "PR规模与测试覆盖",
      "rating": "从 '合理', '较大', '过大' 中选择一个",
      "comment": "评估PR的总体规模是否适中，并根据文件变更列表判断测试覆盖是否充分。例如，是否修改了测试文件，新增功能是否有对应测试。"
    }
  ],
  "missing_info": [
    "如果信息不完整，明确列出缺失的信息点，否则返回空数组 []"
  ]
}

--- 待分析的 PR 信息 ---
标题: {pr_title}
描述:
{pr_description}

--- PR 文件变更摘要 (来自API) ---
{files_info}

--- 关联 Issue 详细信息 (来自API) ---
{issues_info}
`;
    const DEFAULT_MODEL = 'GLM-4.5-flash';
    const DEFAULT_BASE_URL = 'https://open.bigmodel.cn/api/paas/v4/chat/completions';

    // 深度审查专用默认配置（更强的模型）
    const DEFAULT_SECONDARY_MODEL = 'GLM-4.6';
    const DEFAULT_SECONDARY_BASE_URL = 'https://open.bigmodel.cn/api/paas/v4/chat/completions';
    const DEFAULT_SECONDARY_TEMPLATE = `
你是一名资深代码审核专家，具备全面的技术视野。请对提供的 PR 进行专业、深入的代码审核。

## 重要指导原则
### 行号引用优先级策略：
1. **优先使用精确行号引用**：
   - 对于所有能够精确定位的问题，必须使用格式：**L15-L27** 或 **L42**
   - 行号引用要精确到问题所在的具体行，不要随意扩大范围
   - 只有在无法精确定位时，才使用描述性范围

2. **可以使用描述性范围的情况**：
   - Architecture: 整体架构设计问题，如：**整个文件结构**
   - 缺失功能: 应该实现但没有的模块，如：**缺少错误处理模块**
   - 跨文件问题: 涉及多个文件的协调问题，如：**模块间耦合问题**

3. **必须使用精确行号的问题类型**：
   - Security: 具体的输入验证缺失、权限检查漏洞
   - Reliability: 具体的错误处理缺失、边界条件问题
   - Quality: 具体的编码规范、命名、注释问题
   - Performance: 具体的算法效率问题

### 行号引用格式要求：
- 精确引用：**L15-L27** 或 **L42**
- 函数范围：**函数名()** 或 **类中的某个方法**
- 模块范围：**整个文件** 或 **某个代码块**
- 描述性范围仅在无法精确定位时使用

## 审核维度与重点

你需要从以下**四个专业维度**进行系统性检视：

### 1. 安全性检视 🔒
- **输入验证**: 检查用户输入、API参数、文件路径等是否充分验证
- **权限控制**: 评估权限检查、访问控制是否恰当
- **数据泄露风险**: 识别敏感信息泄露、不安全的数据传输
- **注入攻击**: SQL注入、XSS、命令注入、路径遍历等
- **加密与哈希**: 密码存储、数据加密是否安全

### 2. 可靠性检视 🛡️
- **错误处理**: 异常捕获、错误恢复机制是否完善
- **边界条件**: 空值、极值、边界情况的处理
- **资源管理**: 内存泄漏、文件句柄、网络连接管理
- **并发安全**: 线程安全、竞态条件、死锁风险
- **数据一致性**: 事务处理、数据完整性检查

### 3. 架构与设计检视 🏗️
- **代码结构**: 模块化、可维护性、可扩展性
- **设计模式**: 是否恰当使用设计模式，代码组织是否合理
- **接口设计**: API设计、函数签名、参数设计
- **依赖管理**: 循环依赖、过度耦合、职责分离
- **性能考量**: 算法复杂度、性能瓶颈、资源使用效率

### 4. 代码质量检视 ✨
- **编码规范**: 命名规范、代码风格、注释质量
- **测试覆盖**: 测试充分性、边界测试、异常测试
- **可读性**: 代码清晰度、逻辑表达、复杂度控制
- **最佳实践**: 是否遵循语言/框架的最佳实践

## 分析材料

你将获得以下分析材料：
1. **Diff 概览**: 变更统计信息
2. **Diff 分组明细**: 按语言/模块分组的代码变更片段（基于diff）
3. **完整文件对比**: 修改前后文件的完整内容（适用于深度分析）
4. **PR 基本信息**: 标题、描述等上下文信息
5. **关联 Issue**: 相关需求或问题背景

## 分析重点

- **优先使用完整文件内容进行深度分析**，理解代码的整体逻辑和上下文
- **对比修改前后的变化**，评估变更的合理性和影响范围
- **根据问题类型决定是否需要精确行号引用**，不要强行为架构问题找代码行
- **考虑PR的实际业务场景**，评估变更是否真正解决了问题

## 输出要求

1. **深度分析**: 利用完整文件内容，不仅看变更片段，还要理解整体代码逻辑
2. **风险评估**: 对每个分组进行综合风险评估
3. **问题发现**: 根据问题类型决定是否使用具体行号，架构类问题可以描述范围
4. **专业建议**: 提供具体、可操作的修复建议
5. **JSON格式**: 严格按照以下结构输出，不含额外文本

{
  "overall_conclusion": "在 'ok', 'needs_attention', 'critical' 中选择一个",
  "summary": "一句话总结此次 diff 的总体情况，重点关注代码质量和架构影响",
  "group_feedback": [
    {
      "group_title": "分组名称",
      "risk_level": "在 'High', 'Medium', 'Low' 中选择一个",
      "notes": "对该分组的风险点评，必须说明关注点，可包含安全性、可靠性、架构等方面的考量，结合完整文件内容进行分析",
      "files_to_focus": ["需要重点关注的文件路径，至少列出一个，如确实没有则返回空数组并说明理由"]
    }
  ],
  "findings": [
    {
      "file": "文件路径",
      "line_span": "例如 'L15-L27', 'L42', 或 '整个文件', '函数模块' 等",
      "severity": "在 'High', 'Medium', 'Low' 中选择一个",
      "category": "在 'Security', 'Reliability', 'Architecture', 'Quality' 中选择一个",
      "issue": "问题描述，阐明为何可能出错或欠缺，结合完整文件的上下文分析",
      "suggestion": "给出修复/验证建议，考虑整体代码结构"
    }
  ],
  "recommended_checks": [
    "建议开发者或审核者补充的验证步骤（没有可以返回空数组）"
  ]
}

--- PR 基本信息 ---
标题: {pr_title}
描述:
{pr_description}

--- Diff 概览 ---
{diff_overview}

--- Diff 分组明细 ---
{diff_groups_info}

{complete_files_content}

--- 关联 Issue 信息 ---
{issues_info}
`;

// C++ 系统代码专用审核模板
const CPP_SYSTEM_TEMPLATE = `
# 角色
你是一位顶级的 C++ 系统软件审查专家，曾在多个主流操作系统内核团队担任首席工程师。你对 C++ 的内存模型、并发原语、底层机制以及可能导致的未定义行为（Undefined Behavior）了如指掌。你的审查风格以"零信任"和"防御性编程"为核心，旨在发现最隐蔽的资源泄漏、竞态条件和安全漏洞。

# 任务
我将提供一段 C++ 编写的操作系统或底层系统代码。请你以最严苛的标准对其进行审查。你的目标是主动猎杀 Bug，特别是那些在特定硬件、编译器或极端负载下才会暴露的问题。你需要像一位系统黑客、一位追求纳秒级性能的工程师、以及一位负责内核长期稳定性的架构师一样思考。

请基于以下四大核心维度进行系统性审查。在每个维度下，不要简单地检查，而是要提出尖锐的技术问题，并构想可能导致灾难性失败的边缘场景。

## 审查维度与探查指南

### 1. 安全性与内存安全检视 (Security & Memory Safety) 🔒 - "像系统黑客一样思考"

**缓冲区与整数安全:**
- 检查所有裸指针操作: 对 memcpy, strcpy, sprintf 等不安全的 C-style 函数调用保持最高警惕。数据的大小是否由调用者控制？是否存在整数溢出导致分配了过小的缓冲区，从而引发堆/栈溢出？
- 追溯数据来源: 任何来自用户空间、外部设备或网络的数据都是不可信的。在将其用作数组索引、循环计数器或内存分配大小时，是否进行了严格的范围和合理性检查？
- 格式化字符串漏洞: 检查所有 printf 家族的函数，格式化字符串是否是静态常量？如果它部分或全部来自外部输入，是否存在格式化字符串漏洞？

**内核/用户空间边界:**
- 数据拷贝: copy_from_user / copy_to_user (或类似机制) 是否正确处理了错误？在拷贝后，内核是否再次验证了数据的有效性？
- TOCTTOU 攻击 (Time-of-Check-to-Time-of-Use): 在检查一个来自用户空间的指针或权限后，到实际使用它之间，是否存在一个窗口期，用户进程可以修改这块内存或状态，从而绕过检查？

**指针与类型安全:**
- C-style 类型转换: (type*)ptr 强制转换是否安全？reinterpret_cast 是否被滥用，从而破坏了类型系统，导致内存踩踏或未定义行为？
- 空指针解引用: 在解引用任何指针之前，是否都有显式的非空检查？特别是在复杂的函数调用链和错误处理路径中，是否存在某个分支可能导致空指针被使用？

**资源权限与信息泄露:**
- 内核地址泄露: 日志、错误码或返回给用户空间的数据中，是否可能无意中包含了内核空间的指针地址？这会严重削弱 KASLR 等安全机制。
- 能力与权限: 执行该代码路径需要的权限（Capability）是否是最小必需的？是否存在权限提升的漏洞？

### 2. 可靠性与并发检视 (Reliability & Concurrency) 🛡️ - "像高并发系统SRE一样思考"

**资源管理与 RAII:**
- 裸 new/delete: 代码中是否存在手动的 new 和 delete？它们是否可能在异常或复杂的逻辑分支中导致内存泄漏？能否用 std::unique_ptr 或其他遵循 RAII 原则的封装类来保证资源自动释放？
- 超越内存: RAII 是否被一致地用于管理所有资源，包括锁 (std::scoped_lock)、文件句柄、内核对象等？
- 所有权模型: std::unique_ptr vs std::shared_ptr。对于每个动态分配的对象，其所有权模型是否清晰？std::shared_ptr 是否引入了不必要的性能开销或循环引用的风险？原始指针作为观察者使用时，其指向的对象的生命周期是否得到保证？

**错误处理与 noexcept:**
- 异常安全: 在内核或不允许异常的环境中，是否所有函数都标记为 noexcept？如果允许异常，函数的异常安全保证（基本、强、或不抛出）是什么？
- 返回值检查: 对于不使用异常的函数，其返回的错误码是否在每个调用点都得到了妥善处理？是否存在被忽略的错误？

**并发与同步:**
- 数据竞争: 识别所有被多个线程访问的共享数据。每次访问（特别是写入）是否都被 std::mutex, std::atomic 或其他同步原语严格保护？
- 锁的粒度与顺序: 锁的粒度是否过大，导致不必要的性能瓶颈？或者粒度过小，导致保护不完整？系统中所有锁的获取顺序是否全局一致，以从根本上避免死锁？
- 原子操作与内存序: std::atomic 的使用是否正确？是否为性能而选择了过于宽松的内存序，从而在某些CPU架构上导致了难以察觉的指令重排问题？默认的顺序一致性是否是必需的？
- ABA问题: 在无锁（lock-free）数据结构中，是否考虑并处理了ABA问题？

**未定义行为 (Undefined Behavior):**
- 全面排查: 代码中是否存在任何可能触发 UB 的行为？例如：有符号整数溢出、访问野指针/悬垂指针（use-after-free）、在容器迭代时修改容器、对非 union 对象的类型双关（type punning）等。

### 3. 架构与设计检视 (Architecture & Design) 🏗️ - "像内核架构师一样思考"

**抽象与性能:**
- 零成本抽象: C++ 的抽象（类、模板、虚函数）是否带来了不可接受的性能开销？在性能热点路径上，虚函数调用是否可以被模板（编译期多态）或策略模式替代？std::function 的使用是否导致了堆分配和类型擦除的开销？
- 硬件亲和性: 数据结构的设计是否考虑了缓存行的大小，以避免伪共享？代码是否利用了数据局部性原理来提升缓存命中率？

**模块化与层级:**
- 硬件抽象层 (HAL): 平台相关的代码是否与平台无关的逻辑清晰地分离开？
- 头文件依赖: #include 关系是否清晰？是否存在不必要的头文件包含，导致编译时间过长和模块间紧耦合？
- API 与 ABI: 公开的接口是否稳定？修改类的私有成员是否会破坏二进制接口的兼容性？

**C++ 特性应用:**
- 现代 C++ Idioms: 是否充分利用了现代 C++ (C++11/17/20) 提供的更安全、更高效的特性？例如，用 enum class 替代 enum，用 constexpr 进行编译期计算，用移动语义避免不必要的拷贝。
- 模板元编程: 模板的使用是否恰当？是为了实现高效的泛型编程，还是导致了过度复杂、难以调试的"模板魔法"？

### 4. 代码质量与可维护性检视 (Code Quality & Maintainability) ✨ - "像未来的自己一样思考"

**const 正确性 (Const-Correctness):**
- 无处不在的 const: 变量、指针、引用、成员函数是否在可能的情况下都被标记为 const？这不仅能防止意外修改，还能为编译器提供更多优化信息，并极大地增强代码的可读性和可靠性。

**类型与转换:**
- C++ 风格类型转换: 是否使用更安全的 C++ 转换替代了 C-style 的转换？reinterpret_cast 是否只在绝对必要且经过严格审查的情况下使用？

**编码规范与清晰度:**
- 命名: 命名是否清晰地区分了类型、变量、成员变量、宏等？对于系统级代码，命名是否遵循了既有的子系统或社区约定？
- 资源所有权: 从函数签名能否清晰地看出其对传入参数的所有权意图（不拥有、借用、转移、共享）？

**测试与验证:**
- 可测试性: 代码是否易于进行单元测试？对硬件的依赖是否可以通过 mock 或 stub 对象进行解耦？
- 断言 (assert): 是否在关键逻辑的入口和出口使用 assert 来声明不变量、前置条件和后置条件？

## 最终输出要求

对于发现的每一个问题，请按照以下格式进行结构化输出，并按风险等级排序：

**风险等级**: [灾难性 / 高 / 中 / 低]
**问题标题**: (一句话总结问题)
**问题维度**: [安全性 / 可靠性 / 架构 / 质量]
**技术细节**: (深入解释问题，引用 C++ 标准或操作系统原理，说明触发场景和最坏后果)
**代码定位**: (引用有问题的 C++ 代码片段，并标明行号)
**修复建议**: (提供具体的 C++ 修复方案，附上优化后的代码示例)

审查结束后，请给出一个整体评估，并指出你认为最关键的三个修复点，因为它们直接关系到系统的核心稳定性和安全性。

请按照以下 JSON 格式输出：
{
  "overall_conclusion": "在 'ok', 'needs_attention', 'critical' 中选择一个",
  "summary": "一句话总结此次 C++ 系统代码审查的总体评估，重点关注内存安全、并发正确性和系统稳定性",
  "critical_issues": [
    {
      "risk_level": "在 '灾难性', '高', '中', '低' 中选择一个",
      "title": "问题标题",
      "dimension": "在 '安全性', '可靠性', '架构', '质量' 中选择一个",
      "technical_details": "技术细节和影响分析",
      "code_location": "文件路径和行号",
      "fix_suggestion": "具体修复建议和代码示例"
    }
  ],
  "group_feedback": [
    {
      "group_title": "C++模块分组名称",
      "risk_level": "在 'High', 'Medium', 'Low' 中选择一个",
      "notes": "对该C++模块的专业风险评估，结合内存模型、并发安全和系统稳定性分析",
      "files_to_focus": ["需要重点关注的C++文件路径"]
    }
  ],
  "recommended_checks": [
    "建议补充的C++系统级验证步骤，如静态分析、内存检查、并发测试等"
  ]
}

--- PR 基本信息 ---
标题: {pr_title}
描述:
{pr_description}

--- Diff 概览 ---
{diff_overview}

--- C++ 代码分组明细 ---
{diff_groups_info}

{complete_files_content}

--- 关联 Issue 信息 ---
{issues_info}
`;

    // --- 脚本状态 ---
    let lastAnalysisResult = null; // Cache for the last result
    let lastAnalysisContext = null; // Cache for the last analysis context (PR metadata)
    let lastSecondaryFragment = null; // Cache for rendered secondary review DOM snapshot
    let lastAnalyzedPrUrl = null; // Cache for the last analyzed PR URL
    let lastAnalyzedPrUpdatedAt = null; // Cache for the last analyzed PR update time
    let submittedReviews = new Set(); // 跟踪已提交的意见，防止重复提交

    // 渐进式缓存状态
    let analysisState = {
        prDetails: null,        // 缓存 PR 基本信息
        filesData: null,        // 缓存文件列表
        issuesData: null,       // 缓存 Issue 信息
        llmResult: null,        // 缓存 LLM 分析结果
        secondaryResult: null,  // 缓存深度审查结果
        currentStep: 'idle',    // 当前分析步骤: idle, fetching_pr, fetching_files, fetching_issues, analyzing_llm, analyzing_secondary, completed
        startTime: null,        // 开始时间
        lastUpdated: null       // 最后更新时间
    };

    let debugMode = false; // 调试模式开关
    let debugLogs = []; // 调试日志缓存

    // --- API 频率控制 ---
    let lastApiCallTime = 0;
    const API_RATE_LIMIT_DELAY = 200; // 每次API调用间隔200ms
    const MAX_CONCURRENT_REQUESTS = 3; // 最大并发请求数
    const API_RETRY_DELAY = 2000; // API限流时重试延迟2秒
    const MAX_RETRIES = 2; // 最大重试次数

    // 网络状态检测
    function isOnline() {
        return navigator.onLine;
    }

    function waitForNetwork(timeout = 5000) {
        return new Promise((resolve) => {
            if (isOnline()) {
                resolve(true);
                return;
            }

            const checkInterval = setInterval(() => {
                if (isOnline()) {
                    clearInterval(checkInterval);
                    resolve(true);
                }
            }, 1000);

            setTimeout(() => {
                clearInterval(checkInterval);
                resolve(false);
            }, timeout);
        });
    }

    // 带延迟的API请求包装器
    async function rateLimitedApiCall(requestFn, description = 'API调用') {
        const now = Date.now();
        const timeSinceLastCall = now - lastApiCallTime;

        if (timeSinceLastCall < API_RATE_LIMIT_DELAY) {
            const delay = API_RATE_LIMIT_DELAY - timeSinceLastCall;
            logDebug('API频率控制', `${description} 延迟 ${delay}ms 以避免限流`);
            await new Promise(resolve => setTimeout(resolve, delay));
        }

        lastApiCallTime = Date.now();
        logDebug('API频率控制', `${description} 开始执行`);

        return requestFn();
    }

    // 带重试机制的API调用（增强版）
    async function apiCallWithRetry(requestFn, description = 'API调用', retryCount = 0) {
        try {
            // 检查网络状态
            if (!isOnline()) {
                logDebug('API重试', `${description} 网络离线，等待网络恢复...`);
                const networkRestored = await waitForNetwork();
                if (!networkRestored) {
                    throw new Error('网络不可用，请检查网络连接');
                }
            }

            return await rateLimitedApiCall(requestFn, description);
        } catch (error) {
            const errorMessage = error.message || '';

            // 判断是否为可重试的错误
            const isRetryableError = (
                errorMessage.includes('1305') ||
                errorMessage.includes('请求过多') ||
                errorMessage.includes('请稍后重试') ||
                errorMessage.includes('rate limit') ||
                errorMessage.includes('Failed to fetch') ||
                errorMessage.includes('网络请求失败') ||
                errorMessage.includes('超时') ||
                error.status === 429 ||
                error.status === 500 ||
                error.status === 502 ||
                error.status === 503
            );

            if (isRetryableError && retryCount < MAX_RETRIES) {
                const retryDelay = API_RETRY_DELAY * Math.pow(2, retryCount); // 指数退避
                logDebug('API重试', `${description} 遇到可重试错误，${retryDelay}ms 后重试 (${retryCount + 1}/${MAX_RETRIES})`, {
                    error: errorMessage,
                    retryCount,
                    nextRetryDelay: retryDelay
                });

                await new Promise(resolve => setTimeout(resolve, retryDelay));
                return apiCallWithRetry(requestFn, description, retryCount + 1);
            }

            // 不可重试的错误或已达到最大重试次数
            logDebug('API重试', `${description} 重试失败，抛出错误`, {
                error: errorMessage,
                retryCount,
                isRetryableError
            });

            throw error;
        }
    }

    // 检测 PR 是否发生变更
    function hasPrChanged(prDetails) {
        const currentPrUrl = window.location.href;
        const currentUpdatedAt = prDetails?.updated_at;

        logDebug('缓存检测', '检查 PR 是否变更', {
            currentPrUrl,
            lastAnalyzedPrUrl,
            currentUpdatedAt,
            lastAnalyzedPrUpdatedAt
        });

        // 如果 URL 或更新时间发生变化，说明 PR 变更了
        if (currentPrUrl !== lastAnalyzedPrUrl || currentUpdatedAt !== lastAnalyzedPrUpdatedAt) {
            logDebug('缓存检测', 'PR 已变更，清除缓存', {
                urlChanged: currentPrUrl !== lastAnalyzedPrUrl,
                timeChanged: currentUpdatedAt !== lastAnalyzedPrUpdatedAt
            });
            return true;
        }

        logDebug('缓存检测', 'PR 未变更，保持缓存');
        return false;
    }

    // 更新 PR 分析状态
    function updatePrAnalysisState(prDetails) {
        lastAnalyzedPrUrl = window.location.href;
        lastAnalyzedPrUpdatedAt = prDetails?.updated_at;

        logDebug('缓存检测', '更新 PR 分析状态', {
            url: lastAnalyzedPrUrl,
            updatedAt: lastAnalyzedPrUpdatedAt
        });
    }

    // 渐进式缓存管理
    function updateAnalysisStep(step, data = null) {
        analysisState.currentStep = step;
        analysisState.lastUpdated = Date.now();

        if (data !== null) {
            switch (step) {
                case 'fetching_pr':
                    analysisState.prDetails = data;
                    break;
                case 'fetching_files':
                    analysisState.filesData = data;
                    break;
                case 'fetching_issues':
                    analysisState.issuesData = data;
                    break;
                case 'analyzing_llm':
                    analysisState.llmResult = data;
                    break;
                case 'analyzing_secondary':
                    analysisState.secondaryResult = data;
                    break;
                case 'completed':
                    // 完成时，将结果同步到传统缓存变量
                    if (analysisState.llmResult) {
                        lastAnalysisResult = analysisState.llmResult;
                    }
                    if (analysisState.secondaryResult) {
                        lastSecondaryFragment = analysisState.secondaryResult;
                    }
                    break;
            }
        }

        logDebug('渐进式缓存', `更新分析步骤: ${step}`, {
            step,
            hasPrDetails: !!analysisState.prDetails,
            hasFilesData: !!analysisState.filesData,
            hasIssuesData: !!analysisState.issuesData,
            hasLlmResult: !!analysisState.llmResult,
            hasSecondaryResult: !!analysisState.secondaryResult,
            duration: analysisState.startTime ? Date.now() - analysisState.startTime : null
        });
    }

    function startAnalysis(prDetails) {
        analysisState = {
            prDetails: prDetails,
            filesData: null,
            issuesData: null,
            llmResult: null,
            secondaryResult: null,
            currentStep: 'fetching_pr',
            startTime: Date.now(),
            lastUpdated: Date.now()
        };

        // 同步 PR 状态到传统缓存
        updatePrAnalysisState(prDetails);

        logDebug('渐进式缓存', '开始新的分析', {
            prUrl: window.location.href,
            updatedAt: prDetails?.updated_at
        });
    }

    function clearAnalysisCache() {
        analysisState = {
            prDetails: null,
            filesData: null,
            issuesData: null,
            llmResult: null,
            secondaryResult: null,
            currentStep: 'idle',
            startTime: null,
            lastUpdated: null
        };

        logDebug('渐进式缓存', '清除分析缓存');
    }

    function canResumeAnalysis(prDetails) {
        if (!analysisState.prDetails || !analysisState.currentStep || analysisState.currentStep === 'idle') {
            return false;
        }

        // 检查是否是同一个 PR 且没有更新
        const isSamePr = window.location.href === lastAnalyzedPrUrl;
        const isNotUpdated = analysisState.prDetails?.updated_at === prDetails?.updated_at;

        return isSamePr && isNotUpdated;
    }

    function resumeFromCache(prDetails) {
        if (!canResumeAnalysis(prDetails)) {
            return null;
        }

        logDebug('渐进式缓存', '从缓存恢复分析', {
            currentStep: analysisState.currentStep,
            hasPrDetails: !!analysisState.prDetails,
            hasFilesData: !!analysisState.filesData,
            hasIssuesData: !!analysisState.issuesData,
            hasLlmResult: !!analysisState.llmResult,
            hasSecondaryResult: !!analysisState.secondaryResult
        });

        return analysisState;
    }

    // LLM 分析调用（封装为 Promise，增强错误处理）
    function callLLMAnalysis(config, prompt) {
        return new Promise((resolve, reject) => {
            const requestDetails = {
                method: 'POST',
                url: config.baseUrl,
                headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${config.apiKey}` },
                data: JSON.stringify({ model: config.model, messages: [{ role: 'user', content: prompt }], stream: false }),
                timeout: 60000, // 60秒超时
                onload: function(response) {
                    try {
                        logDebug('LLM请求', '收到响应', {
                            status: response.status,
                            statusText: response.statusText,
                            responseLength: response.responseText?.length || 0
                        });

                        if (response.status >= 200 && response.status < 300) {
                            const data = JSON.parse(response.responseText);
                            if (data.choices && data.choices[0] && data.choices[0].message) {
                                const reviewContent = data.choices[0].message.content;
                                const cleanMessage = reviewContent.replace(/```json/g, '').replace(/```/g, '').trim();
                                const jsonData = JSON.parse(cleanMessage);
                                resolve(jsonData);
                            } else {
                                reject(new Error('LLM 响应格式不正确：缺少 choices 或 message'));
                            }
                        } else {
                            let errorMsg = `HTTP ${response.status}: ${response.statusText}`;
                            try {
                                const errorData = JSON.parse(response.responseText);
                                errorMsg = errorData.error?.message || errorData.message || errorMsg;
                            } catch (e) {
                                // 使用默认错误信息
                            }
                            reject(new Error(`LLM 请求失败: ${errorMsg}`));
                        }
                    } catch (error) {
                        logDebug('LLM请求', '解析响应失败', {
                            error: error.message,
                            responseLength: response.responseText?.length || 0,
                            responsePreview: response.responseText?.slice(0, 200)
                        });
                        reject(new Error(`解析 LLM 响应失败: ${error.message}`));
                    }
                },
                onerror: function(err) {
                    logDebug('LLM请求', '网络错误', {
                        error: err.toString(),
                        url: config.baseUrl
                    });
                    reject(new Error(`LLM 网络请求失败: ${err}`));
                },
                ontimeout: function() {
                    logDebug('LLM请求', '请求超时', { url: config.baseUrl });
                    reject(new Error('LLM 请求超时，请检查网络连接或重试'));
                }
            };

            try {
                GM_xmlhttpRequest(requestDetails);
            } catch (setupError) {
                logDebug('LLM请求', '设置请求失败', { error: setupError.message });
                reject(new Error(`LLM 请求设置失败: ${setupError.message}`));
            }
        });
    }

    // 从缓存状态恢复分析
    async function resumeAnalysisFromState(modalBody, config, repoInfo, cachedState) {
        const { primary, secondary } = getModalSections(modalBody);

        try {
            // 根据缓存状态决定从哪个步骤开始
            switch (cachedState.currentStep) {
                case 'completed':
                    // 完全恢复
                    if (cachedState.llmResult) {
                        renderContent(primary, cachedState.llmResult);
                        lastAnalysisResult = cachedState.llmResult;
                    }
                    if (config.secondaryReviewEnabled && cachedState.secondaryResult) {
                        secondary.innerHTML = '';
                        secondary.appendChild(cachedState.secondaryResult.cloneNode(true));
                        lastSecondaryFragment = cachedState.secondaryResult;
                    }
                    updateProgress(modalBody, '从缓存恢复完成');
                    break;

                case 'analyzing_secondary':
                    // 恢复到深度审查前
                    if (cachedState.llmResult) {
                        renderContent(primary, cachedState.llmResult);
                        lastAnalysisResult = cachedState.llmResult;
                    }
                    if (config.secondaryReviewEnabled) {
                        await runSecondaryAnalysisWithCache(modalBody, config, repoInfo, {
                            repoInfo,
                            prTitle: cachedState.prDetails?.title,
                            prDescription: cachedState.prDetails?.body,
                            issuesInfoText: cachedState.issuesData
                        });
                    }
                    break;

                case 'analyzing_llm':
                    // 重新进行 LLM 分析
                    if (cachedState.filesData && cachedState.issuesData !== null) {
                        const prTitle = cachedState.prDetails?.title;
                        const prDescription = cachedState.prDetails?.body || '（无描述）';
                        const fileList = cachedState.filesData.diffs.map(d => `- ${d.statistic.type}: ${d.statistic.path} (+${d.added_lines} / -${d.remove_lines})`).join('\n');
                        const filesInfoText = `总体变更: ${cachedState.filesData.count} 个文件, +${cachedState.filesData.added_lines} 行, -${cachedState.filesData.remove_lines} 行.\n\n文件列表:\n${fileList}`;

                        updateProgress(modalBody, '继续 LLM 分析...');
                        const prompt = config.template
                            .replace('{pr_title}', prTitle)
                            .replace('{pr_description}', prDescription)
                            .replace('{files_info}', filesInfoText)
                            .replace('{issues_info}', cachedState.issuesData);

                        const llmResult = await callLLMAnalysis(config, prompt);
                        renderContent(primary, llmResult);
                        updateAnalysisStep('analyzing_llm', llmResult);
                        lastAnalysisResult = llmResult;

                        // 继续深度审查
                        if (config.secondaryReviewEnabled) {
                            await runSecondaryAnalysisWithCache(modalBody, config, repoInfo, {
                                repoInfo,
                                prTitle,
                                prDescription,
                                issuesInfoText: cachedState.issuesData
                            });
                        }
                    }
                    break;

                default:
                    // 从当前步骤继续
                    updateProgress(modalBody, `从步骤 "${cachedState.currentStep}" 继续分析...`);
                    // 这里可以实现更细致的步骤恢复逻辑
                    break;
            }
        } catch (error) {
            logDebug('渐进式缓存', '恢复分析失败', { error: error.message });
            // 如果恢复失败，重新开始完整分析
            updateProgress(modalBody, '恢复失败，重新开始分析...');
            clearAnalysisCache();
            // 递归调用重新开始（避免无限循环）
            setTimeout(() => runAnalysis(), 100);
        }
    }

    // 带缓存的深度审查分析
    async function runSecondaryAnalysisWithCache(modalBody, config, repoInfo, context) {
        const { secondary } = getModalSections(modalBody);
        if (!secondary) return;

        logDebug('深度审查', '开始带缓存的深度审查', {
            hasCachedResult: !!analysisState.secondaryResult
        });

        // 如果有缓存结果，直接使用
        if (analysisState.secondaryResult) {
            secondary.innerHTML = '';
            secondary.appendChild(analysisState.secondaryResult.cloneNode(true));
            return;
        }

        // 否则进行深度审查
        await runSecondaryAnalysis(modalBody, config, repoInfo, context);
    }

    // 调试模式控制
    function setDebugMode(enabled) {
        debugMode = enabled;
        if (enabled) {
            console.log('🐛 调试模式已启用');
            logDebug('调试模式', '调试模式已启用');
        }
    }

    // 调试日志记录
    function logDebug(category, message, data = null) {
        const timestamp = new Date().toISOString();
        const logEntry = {
            timestamp,
            category,
            message,
            data: data ? JSON.stringify(data, null, 2) : null
        };

        debugLogs.push(logEntry);

        // 只保留最近100条日志
        if (debugLogs.length > 100) {
            debugLogs = debugLogs.slice(-100);
        }

        if (debugMode) {
            console.log(`🐛 [${category}] ${message}`, data || '');
        }
    }

    // 获取调试日志
    function getDebugLogs() {
        return [...debugLogs];
    }

    // 清空调试日志
    function clearDebugLogs() {
        debugLogs = [];
        logDebug('调试模式', '调试日志已清空');
    }

    // 显示调试信息面板
    function showDebugPanel() {
        const logs = getDebugLogs();
        const panelHTML = `
            <div id="debug-panel-overlay" class="llm-modal-overlay" style="z-index: 10000;">
                <div class="llm-modal-content" style="width: 800px; max-height: 80vh;">
                    <div class="llm-modal-header">
                        <h3>🐛 调试信息面板</h3>
                        <div>
                            <button onclick="clearDebugLogs()" style="cursor: pointer; border: none; background: none; font-size: 14px; margin-right: 10px;">🗑️ 清空</button>
                            <button onclick="exportDebugLogs()" style="cursor: pointer; border: none; background: none; font-size: 14px; margin-right: 10px;">📥 导出</button>
                            <button onclick="this.closest('.llm-modal-overlay').remove()" style="cursor: pointer; border: none; background: none; font-size: 18px;">❌</button>
                        </div>
                    </div>
                    <div class="llm-modal-body" style="padding: 15px;">
                        <div style="margin-bottom: 15px;">
                            <strong>总日志数:</strong> ${logs.length} |
                            <strong>调试模式:</strong> ${debugMode ? '开启' : '关闭'} |
                            <button onclick="setDebugMode(!debugMode)" style="margin-left: 10px; padding: 4px 8px; font-size: 12px;">切换调试模式</button>
                        </div>
                        <div class="llm-table-container" style="max-height: 400px;">
                            <table style="width: 100%; font-size: 12px;">
                                <thead>
                                    <tr style="background: #f8f9fa;">
                                        <th style="padding: 8px; text-align: left;">时间</th>
                                        <th style="padding: 8px; text-align: left;">分类</th>
                                        <th style="padding: 8px; text-align: left;">消息</th>
                                        <th style="padding: 8px; text-align: left;">操作</th>
                                    </tr>
                                </thead>
                                <tbody>
                                    ${logs.slice(-50).reverse().map(log => `
                                        <tr style="border-bottom: 1px solid #eee;">
                                            <td style="padding: 6px; font-family: monospace; font-size: 10px;">${new Date(log.timestamp).toLocaleTimeString()}</td>
                                            <td style="padding: 6px;"><span style="background: #e3f2fd; padding: 2px 4px; border-radius: 3px; font-size: 10px;">${log.category}</span></td>
                                            <td style="padding: 6px; word-break: break-all;">${log.message}</td>
                                            <td style="padding: 6px;">
                                                ${log.data ? `<button onclick="alert(${JSON.stringify(log.data)})" style="padding: 2px 4px; font-size: 10px;">查看</button>` : '-'}
                                            </td>
                                        </tr>
                                    `).join('')}
                                </tbody>
                            </table>
                        </div>
                    </div>
                </div>
            </div>
        `;

        // 移除已存在的调试面板
        document.getElementById('debug-panel-overlay')?.remove();
        document.body.insertAdjacentHTML('beforeend', panelHTML);
    }

    // 导出调试日志
    function exportDebugLogs() {
        const logs = getDebugLogs();
        const blob = new Blob([JSON.stringify(logs, null, 2)], { type: 'application/json' });
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = `gitcode-review-debug-${new Date().toISOString().slice(0, 19)}.json`;
        a.click();
        URL.revokeObjectURL(url);
    }

    // --- 脚本主要逻辑 ---

    function injectStyles() {
        if (document.getElementById('llm-review-styles')) return;
        const style = document.createElement('style');
        style.id = 'llm-review-styles';
        style.textContent = `
            .llm-modal-overlay {
                position: fixed; top: 0; left: 0; width: 100%; height: 100%;
                background: rgba(0,0,0,0.6); z-index: 9999; display: flex;
                align-items: center; justify-content: center; padding: 20px;
                box-sizing: border-box;
            }
            .llm-modal-content {
                background: #f8f9fa; padding: 0; border-radius: 8px; width: 900px;
                max-width: 95vw; max-height: 90vh; overflow: hidden; display: flex;
                flex-direction: column; box-shadow: 0 5px 15px rgba(0,0,0,0.3);
            }
            .llm-modal-header {
                display: flex; justify-content: space-between; align-items: center;
                padding: 12px 20px; border-bottom: 1px solid #dee2e6; background: #fff;
                flex-shrink: 0;
            }
            .llm-modal-header h3 { margin: 0; font-size: 18px; font-weight: 600; }
            .llm-modal-body {
                padding: 20px;
                overflow-y: auto;
                flex-grow: 1;
                min-height: 0;
                word-wrap: break-word;
                overflow-x: hidden;
            }
            .llm-spinner {
                border: 4px solid #f3f3f3; border-top: 4px solid #0366d6;
                border-radius: 50%; width: 40px; height: 40px;
                animation: spin 1s linear infinite; margin: 20px auto;
            }
            @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
            .llm-progress-view {
                display: flex; flex-direction: column; align-items: center;
                justify-content: center; padding: 40px; color: #6c757d;
            }
            .llm-summary-card {
                padding: 15px; border: 1px solid #ddd; border-radius: 6px;
                background-color: #fff; margin-bottom: 20px;
                word-wrap: break-word;
                overflow-wrap: break-word;
            }
            .llm-assessment {
                padding: 5px 12px; border-radius: 15px; color: white;
                font-weight: bold; display: inline-block; font-size: 14px;
            }
            .llm-feedback-card {
                background: #fff; border: 1px solid #e9ecef; border-radius: 6px;
                margin-bottom: 12px; padding: 15px;
                word-wrap: break-word;
                overflow-wrap: break-word;
            }
            .llm-feedback-card-header {
                font-weight: 600; display: flex; align-items: center; gap: 8px;
                flex-wrap: wrap;
            }
            .llm-feedback-card-header span { font-weight: normal; margin-left: auto; }
            .llm-feedback-card-comment {
                margin: 8px 0 0 5px;
                color: #495057;
                word-wrap: break-word;
                overflow-wrap: break-word;
                white-space: pre-wrap;
            }
            .llm-missing-info-card {
                background: #fff3cd; border-color: #ffeeba; padding: 15px;
                border-radius: 6px; color: #856404; margin-top: 20px;
                word-wrap: break-word;
                overflow-wrap: break-word;
            }

            /* 优化表格显示 */
            .llm-findings-table {
                width: 100%;
                border-collapse: collapse;
                font-size: 13px;
                min-width: 800px;
                word-wrap: break-word;
                overflow-wrap: break-word;
            }
            .llm-findings-table th,
            .llm-findings-table td {
                padding: 6px 8px;
                text-align: left;
                border-bottom: 1px solid #e9ecef;
                vertical-align: top;
                max-width: 200px;
                word-wrap: break-word;
                overflow-wrap: break-word;
            }
            .llm-findings-table th {
                font-weight: 600;
                background-color: #f8f9fa;
                position: sticky;
                top: 0;
                z-index: 1;
            }
            .llm-findings-table code {
                background-color: #f1f3f4;
                padding: 2px 4px;
                border-radius: 3px;
                font-size: 12px;
                word-break: break-all;
                display: inline-block;
                max-width: 100%;
            }
            .llm-findings-table .issue-text,
            .llm-findings-table .suggestion-text {
                white-space: pre-wrap;
                word-wrap: break-word;
                overflow-wrap: break-word;
                line-height: 1.4;
            }

            /* 表格容器 */
            .llm-table-container {
                overflow-x: auto;
                margin: 10px 0;
                border: 1px solid #e9ecef;
                border-radius: 6px;
                max-height: 400px;
                overflow-y: auto;
            }

            /* 代码片段优化 */
            .llm-code-snippet {
                background-color: #212529;
                color: #f8f9fa;
                padding: 10px;
                border-radius: 4px;
                overflow-x: auto;
                font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
                font-size: 12px;
                line-height: 1.4;
                white-space: pre;
                word-wrap: normal;
                overflow-wrap: normal;
            }

            /* 提交检视意见按钮样式 */
            .llm-submit-review-btn {
                transition: all 0.2s ease;
                font-family: inherit;
            }
            .llm-submit-review-btn:hover {
                background-color: #0256cc !important;
                transform: scale(1.05);
            }
            .llm-submit-review-btn:active {
                transform: scale(0.95);
            }
            .llm-submit-review-btn:disabled {
                cursor: not-allowed;
                opacity: 0.7;
                transform: none !important;
            }

            /* 调试按钮样式 */
            .llm-debug-btn {
                position: fixed;
                bottom: 90px;
                right: 30px;
                width: 40px;
                height: 40px;
                background-color: #6c757d;
                color: white;
                border-radius: 50%;
                display: flex;
                align-items: center;
                justify-content: center;
                font-size: 16px;
                cursor: pointer;
                box-shadow: 0 2px 6px rgba(0,0,0,0.2);
                z-index: 9997;
                transition: transform 0.2s ease-in-out;
                opacity: 0.7;
            }
            .llm-debug-btn:hover {
                transform: scale(1.1);
                opacity: 1;
            }
            .llm-debug-btn.active {
                background-color: #28a745;
                opacity: 1;
            }

            /* 错误提示样式 */
            .llm-error-toast {
                position: fixed;
                top: 20px;
                right: 20px;
                background: #dc3545;
                color: white;
                padding: 15px 20px;
                border-radius: 6px;
                box-shadow: 0 4px 12px rgba(0,0,0,0.3);
                z-index: 10001;
                max-width: 400px;
                font-size: 14px;
                opacity: 0;
                transform: translateX(100%);
                transition: all 0.3s ease;
            }
            .llm-error-toast.show {
                opacity: 1;
                transform: translateX(0);
            }
            .llm-error-toast .error-title {
                font-weight: 600;
                margin-bottom: 5px;
            }
            .llm-error-toast .error-message {
                margin-bottom: 10px;
            }
            .llm-error-toast .error-actions {
                display: flex;
                gap: 10px;
                flex-wrap: wrap;
            }
            .llm-error-toast button {
                background: rgba(255,255,255,0.2);
                border: 1px solid rgba(255,255,255,0.3);
                color: white;
                padding: 4px 8px;
                border-radius: 4px;
                font-size: 12px;
                cursor: pointer;
            }
            .llm-error-toast button:hover {
                background: rgba(255,255,255,0.3);
            }
            .llm-info-toast {
                position: fixed;
                top: 20px;
                right: 20px;
                background: #17a2b8;
                color: white;
                padding: 15px 20px;
                border-radius: 6px;
                box-shadow: 0 4px 12px rgba(0,0,0,0.3);
                z-index: 10001;
                max-width: 400px;
                font-size: 14px;
                opacity: 0;
                transform: translateX(100%);
                transition: all 0.3s ease;
            }
            .llm-info-toast.show {
                opacity: 1;
                transform: translateX(0);
            }
            .llm-info-toast .info-title {
                font-weight: 600;
                margin-bottom: 5px;
            }
            .llm-info-toast .info-message {
                line-height: 1.4;
            }

            /* 响应式优化 */
            @media (max-width: 768px) {
                .llm-modal-content {
                    width: 95vw;
                    max-height: 95vh;
                }
                .llm-modal-body {
                    padding: 15px;
                }
                .llm-findings-table th,
                .llm-findings-table td {
                    padding: 4px 6px;
                    font-size: 12px;
                }
                .llm-submit-review-btn {
                    font-size: 10px;
                    padding: 3px 6px;
                }
            }
        `;
        document.head.appendChild(style);
    }

    const originalAppendChild = Node.prototype.appendChild;
    Node.prototype.appendChild = function(child) {
        if (typeof child === 'string') {
            child = document.createTextNode(child);
        }
        return originalAppendChild.call(this, child);
    };

    async function main() {
        const existingModal = document.getElementById('llm-result-modal-overlay');
        if (existingModal) {
            existingModal.remove();
            return;
        }

        if (lastAnalysisResult) {
            const config = await getConfig();
            const { body: modalBody } = showResultModal();
            renderContent(modalBody, lastAnalysisResult);
            const { secondary } = getModalSections(modalBody);
            if (config && config.secondaryReviewEnabled) {
                if (lastSecondaryFragment) {
                    secondary.innerHTML = '';
                    secondary.appendChild(lastSecondaryFragment.cloneNode(true));
                } else if (lastAnalysisContext) {
                    runSecondaryAnalysis(modalBody, config, lastAnalysisContext.repoInfo, lastAnalysisContext);
                } else {
                    secondary.innerHTML = '';
                }
            } else {
                secondary.innerHTML = '';
            }
            return;
        }

        await runAnalysis();
    }

    function addFloatingActionButton() {
        if (document.getElementById('llm-review-fab')) return;
        const fab = document.createElement('div');
        fab.id = 'llm-review-fab';
        fab.innerText = '🤖';
        fab.title = 'LLM 深度审核 (含Issue)';
        fab.style.cssText = `
            position: fixed; bottom: 30px; right: 30px; width: 56px; height: 56px;
            background-color: #0366d6; color: white; border-radius: 50%;
            display: flex; align-items: center; justify-content: center; font-size: 28px;
            cursor: pointer; box-shadow: 0 4px 8px rgba(0,0,0,0.2); z-index: 9998;
            transition: transform 0.2s ease-in-out;
        `;
        fab.onmouseover = () => { fab.style.transform = 'scale(1.1)'; };
        fab.onmouseout = () => { fab.style.transform = 'scale(1)'; };
        fab.onclick = main;
        document.body.appendChild(fab);

        // 添加调试按钮
        addDebugButton();
    }

    function addDebugButton() {
        if (document.getElementById('llm-debug-btn')) return;

        const debugBtn = document.createElement('div');
        debugBtn.id = 'llm-debug-btn';
        debugBtn.innerText = '🐛';
        debugBtn.title = '调试模式 (查看详细日志)';
        debugBtn.className = 'llm-debug-btn';

        // 根据调试模式状态更新按钮样式
        const updateDebugButton = () => {
            if (debugMode) {
                debugBtn.classList.add('active');
            } else {
                debugBtn.classList.remove('active');
            }
        };

        debugBtn.onclick = () => {
            if (debugMode) {
                showDebugPanel();
            } else {
                setDebugMode(true);
                updateDebugButton();
                showErrorToast('调试模式已启用', '所有API调用和错误信息将被详细记录。点击调试按钮查看日志。', [
                    { text: '查看日志', action: () => showDebugPanel() },
                    { text: '关闭', action: () => {} }
                ]);
            }
        };

        // 监听调试模式变化
        const originalSetDebugMode = setDebugMode;
        setDebugMode = (enabled) => {
            originalSetDebugMode(enabled);
            updateDebugButton();
        };

        document.body.appendChild(debugBtn);
        updateDebugButton();
    }

    // 显示错误提示
    function showErrorToast(title, message, actions = []) {
        // 移除已存在的错误提示
        const existingToast = document.querySelector('.llm-error-toast');
        if (existingToast) {
            existingToast.remove();
        }

        const toast = document.createElement('div');
        toast.className = 'llm-error-toast';

        const actionsHtml = actions.map(action =>
            `<button onclick="(${action.action.toString()})(); this.closest('.llm-error-toast').remove()">${action.text}</button>`
        ).join('');

        toast.innerHTML = `
            <div class="error-title">🚨 ${title}</div>
            <div class="error-message">${message}</div>
            ${actionsHtml ? `<div class="error-actions">${actionsHtml}</div>` : ''}
        `;

        document.body.appendChild(toast);

        // 触发动画
        setTimeout(() => {
            toast.classList.add('show');
        }, 10);

        // 自动关闭
        setTimeout(() => {
            toast.classList.remove('show');
            setTimeout(() => {
                toast.remove();
            }, 300);
        }, 8000);
    }

    function showInfoToast(title, message) {
        // 移除已存在的信息提示
        const existingToast = document.querySelector('.llm-info-toast');
        if (existingToast) {
            existingToast.remove();
        }

        const toast = document.createElement('div');
        toast.className = 'llm-info-toast';
        toast.innerHTML = `
            <div class="info-title">ℹ️ ${title}</div>
            <div class="info-message">${message}</div>
        `;

        document.body.appendChild(toast);

        // 触发动画
        setTimeout(() => {
            toast.classList.add('show');
        }, 10);

        // 自动关闭
        setTimeout(() => {
            toast.classList.remove('show');
            setTimeout(() => {
                toast.remove();
            }, 300);
        }, 4000); // 4秒后自动关闭
    }

    async function getConfig() {
        const configs = {
            baseUrl: await GM_getValue('LLM_BASE_URL', DEFAULT_BASE_URL),
            apiKey: await GM_getValue('API_KEY', ''),
            model: await GM_getValue('LLM_MODEL', DEFAULT_MODEL),
            template: await GM_getValue('PROMPT_TEMPLATE', DEFAULT_TEMPLATE),
            gitcodeToken: await GM_getValue('GITCODE_ACCESS_TOKEN', ''),
            // Secondary review configs
            secondaryReviewEnabled: await GM_getValue('SECONDARY_REVIEW_ENABLED', false),
            secondaryBaseUrl: await GM_getValue('SECONDARY_LLM_BASE_URL', DEFAULT_SECONDARY_BASE_URL),
            secondaryApiKey: await GM_getValue('SECONDARY_API_KEY', ''),
            secondaryModel: await GM_getValue('SECONDARY_LLM_MODEL', DEFAULT_SECONDARY_MODEL)
        };
        if (!configs.baseUrl || !configs.apiKey || !configs.model || !configs.gitcodeToken) {
            return null;
        }
        return configs;
    }

    function extractRepoInfo() {
        const prUrlRegex = /\/([^\/]+)\/([^\/]+)\/pull\/(\d+)/;
        const prUrlMatch = window.location.pathname.match(prUrlRegex);
        if (!prUrlMatch) {
            console.error("Could not extract repo info from URL.");
            return null;
        }
        return { owner: prUrlMatch[1], repo: prUrlMatch[2], prId: prUrlMatch[3] };
    }

    function fetchPRDetails({ owner, repo, prId }, token) {
        return new Promise((resolve, reject) => {
            const apiUrl = `https://api.gitcode.com/api/v5/repos/${owner}/${repo}/pulls/${prId}?access_token=${token}`;
            GM_xmlhttpRequest({ method: 'GET', url: apiUrl, headers: { 'Accept': 'application/json' },
                onload: res => (res.status >= 200 && res.status < 300) ? resolve(JSON.parse(res.responseText)) : reject(`获取 PR 详细信息失败: ${res.statusText}`),
                onerror: err => reject(`获取 PR 详细信息时网络错误: ${err}`) });
        });
    }

    function fetchFilesInfo({ owner, repo, prId }, token) {
        return new Promise((resolve, reject) => {
            if (!token) {
                logDebug('fetchFilesInfo', 'GitCode Access Token 为空', { owner, repo, prId });
                reject(new Error('GitCode Access Token 为空'));
                return;
            }

            const apiUrl = `https://api.gitcode.com/api/v5/repos/${owner}/${repo}/pulls/${prId}/files.json?access_token=${token}`;
            logDebug('fetchFilesInfo', '开始获取 PR 文件列表', {
                owner,
                repo,
                prId,
                apiUrl: apiUrl.split('?')[0] + '?...'
            });

            const requestDetails = {
                method: 'GET',
                url: apiUrl,
                headers: {
                    'Accept': 'application/json',
                    'User-Agent': 'GitCode-PR-Review-Helper/1.0'
                },
                timeout: 30000, // 30秒超时
                onload: function(res) {
                    logDebug('fetchFilesInfo', '收到 GitCode API 响应', {
                        status: res.status,
                        statusText: res.statusText,
                        owner,
                        repo,
                        prId,
                        responseLength: res.responseText?.length || 0
                    });

                    if (res.status >= 200 && res.status < 300) {
                        try {
                            const data = JSON.parse(res.responseText);
                            logDebug('fetchFilesInfo', '成功解析 PR 文件列表', {
                                filesCount: data.diffs?.length || 0,
                                hasDiffRefs: !!data.diff_refs,
                                addedLines: data.added_lines,
                                removedLines: data.remove_lines
                            });
                            resolve(data);
                        } catch (parseError) {
                            logDebug('fetchFilesInfo', '解析响应失败', {
                                error: parseError.message,
                                responseText: res.responseText.slice(0, 200)
                            });
                            reject(new Error(`解析 PR 文件列表失败: ${parseError.message}`));
                        }
                    } else {
                        let errorMsg = `HTTP ${res.status}: ${res.statusText}`;
                        try {
                            const errorData = JSON.parse(res.responseText);
                            errorMsg = errorData.message || errorData.error?.message || errorMsg;
                        } catch (e) {
                            // 使用默认错误信息
                        }

                        logDebug('fetchFilesInfo', 'API 请求失败', {
                            status: res.status,
                            statusText: res.statusText,
                            error: errorMsg,
                            responseText: res.responseText.slice(0, 200)
                        });

                        // 特殊处理常见错误
                        if (res.status === 401) {
                            errorMsg = 'GitCode Access Token 无效或已过期，请检查配置';
                        } else if (res.status === 403) {
                            errorMsg = '访问被拒绝，请检查 Access Token 权限或仓库访问权限';
                        } else if (res.status === 404) {
                            errorMsg = 'PR 或仓库不存在，请检查页面是否正确';
                        } else if (res.status === 429) {
                            errorMsg = 'API 请求频率过高，请稍后重试';
                        }

                        reject(new Error(`获取 PR 文件列表失败: ${errorMsg}`));
                    }
                },
                onerror: function(err) {
                    logDebug('fetchFilesInfo', '网络请求失败', {
                        error: err.toString(),
                        owner,
                        repo,
                        prId
                    });

                    // 提供更具体的错误建议
                    let errorMsg = `网络请求失败: ${err}`;
                    if (err.toString().includes('Failed to fetch')) {
                        errorMsg = '网络请求失败，可能是：\n1. 网络连接问题\n2. GitCode API 服务不可用\n3. 被 CORS 策略阻止\n4. 被浏览器扩展阻止';
                    }

                    reject(new Error(`获取 PR 文件列表时${errorMsg}`));
                },
                ontimeout: function() {
                    logDebug('fetchFilesInfo', '请求超时', { owner, repo, prId });
                    reject(new Error('获取 PR 文件列表超时，请检查网络连接'));
                }
            };

            try {
                GM_xmlhttpRequest(requestDetails);
            } catch (setupError) {
                logDebug('fetchFilesInfo', '设置请求失败', { error: setupError.message });
                reject(new Error(`设置 PR 文件列表请求失败: ${setupError.message}`));
            }
        });
    }

    // 新增：获取文件的完整内容（基于SHA）
    function fetchFileContent({ owner, repo, filePath, sha }, token) {
        return new Promise((resolve, reject) => {
            if (!token) {
                reject(new Error('GitCode Access Token 为空'));
                return;
            }

            const apiUrl = `https://api.gitcode.com/api/v5/repos/${owner}/${repo}/contents/${encodeURIComponent(filePath)}?access_token=${token}&ref=${sha}`;
            logDebug('文件内容获取', '开始获取文件内容', {
                owner,
                repo,
                filePath,
                sha: sha?.slice(0, 8) + '...',
                apiUrl: apiUrl.split('?')[0] + '?...' // 不记录完整URL以避免泄露token
            });

            GM_xmlhttpRequest({
                method: 'GET',
                url: apiUrl,
                headers: { 'Accept': 'application/json' },
                onload: function(res) {
                    logDebug('文件内容获取', '收到API响应', {
                        status: res.status,
                        statusText: res.statusText,
                        filePath,
                        sha
                    });

                    if (res.status >= 200 && res.status < 300) {
                        try {
                            const data = JSON.parse(res.responseText);
                            if (data.content) {
                                // GitCode API 返回的内容是 base64 编码的
                                const content = atob(data.content);
                                logDebug('文件内容获取', '成功解码文件内容', {
                                    filePath,
                                    contentLength: content.length,
                                    encoding: 'base64 decoded'
                                });
                                resolve({
                                    path: filePath,
                                    content: content,
                                    sha: sha,
                                    size: content.length
                                });
                            } else {
                                const error = new Error(`文件 ${filePath} 不存在或内容为空`);
                                logDebug('文件内容获取', '文件内容为空', { filePath, data });
                                reject(error);
                            }
                        } catch (parseError) {
                            const error = new Error(`解析文件内容失败: ${parseError.message}`);
                            logDebug('文件内容获取', '解析响应失败', {
                                filePath,
                                error: parseError.message,
                                responseText: res.responseText.slice(0, 200)
                            });
                            reject(error);
                        }
                    } else {
                        let errorMsg = `获取文件内容失败: HTTP ${res.status} ${res.statusText}`;
                        try {
                            const errorData = JSON.parse(res.responseText);
                            errorMsg = errorData.message || errorMsg;
                        } catch (e) {
                            // 忽略解析错误
                        }

                        const error = new Error(errorMsg);
                        error.status = res.status; // 添加状态码便于识别限流错误
                        logDebug('文件内容获取', 'API请求失败', {
                            filePath,
                            sha,
                            status: res.status,
                            statusText: res.statusText,
                            error: errorMsg
                        });
                        reject(error);
                    }
                },
                onerror: function(err) {
                    const error = new Error(`网络请求失败: ${err}`);
                    logDebug('文件内容获取', '网络错误', { filePath, sha, error: err.toString() });
                    reject(error);
                }
            });
        });
    }

    // 新增：批量获取修改前后的文件内容
    async function fetchModifiedFilesContent({ owner, repo, prId }, filesData, token, fileStatuses = null) {
        logDebug('批量文件获取', '开始分析文件数据', {
            hasFilesData: !!filesData,
            hasDiffRefs: !!filesData?.diff_refs,
            hasDiffs: !!filesData?.diffs,
            diffsCount: filesData?.diffs?.length || 0,
            repoInfo: { owner, repo, prId }
        });

        if (!filesData.diff_refs || !filesData.diffs) {
            const error = new Error('缺少必要的refs或diffs信息');
            logDebug('批量文件获取', '缺少必要信息', {
                hasDiffRefs: !!filesData?.diff_refs,
                hasDiffs: !!filesData?.diffs,
                filesDataKeys: Object.keys(filesData || {})
            });
            throw error;
        }

        const { base_sha, head_sha } = filesData.diff_refs;

        if (!base_sha || !head_sha) {
            const error = new Error(`缺少必要的SHA引用: base_sha=${base_sha ? '已获取' : '缺失'}, head_sha=${head_sha ? '已获取' : '缺失'}`);
            logDebug('批量文件获取', 'SHA引用缺失', { base_sha, head_sha });
            throw error;
        }
        // 首先诊断实际的文件类型
        const actualFileTypes = filesData.diffs.map(diff => diff.statistic?.type);
        logDebug('批量文件获取', '文件类型诊断', {
            allTypes: actualFileTypes,
            uniqueTypes: [...new Set(actualFileTypes)],
            sampleFile: filesData.diffs[0]?.statistic
        });

        // 更严格的文件过滤：只处理真正需要获取内容的文件类型
        const modifiedFiles = filesData.diffs.filter(diff => {
            const filePath = diff.statistic.new_path || diff.statistic.old_path;
            let type = diff.statistic?.type;

            // 尝试从传入的文件状态信息中获取正确的变更类型
            if (fileStatuses && fileStatuses.length > 0) {
                const matchingStatus = fileStatuses.find(status =>
                    (status.path === filePath) ||
                    (status.newPath === diff.statistic.new_path) ||
                    (status.oldPath === diff.statistic.old_path) ||
                    (status.path === (diff.statistic.new_path || diff.statistic.old_path))
                );

                if (matchingStatus) {
                    type = matchingStatus.status;
                    logDebug('批量文件获取', '过滤阶段使用正确的文件状态', {
                        filePath,
                        originalType: diff.statistic?.type,
                        correctedType: type,
                        statusSource: 'fileStatuses'
                    });
                }
            }

            // 如果没有状态信息，根据文件扩展名跳过二进制文件
            if (!fileStatuses || fileStatuses.length === 0) {
                const skipTypes = ['unknown', 'binary', 'image'];
                if (skipTypes.includes(type?.toLowerCase())) {
                    logDebug('批量文件获取', '跳过不需要内容分析的文件', { filePath, type });
                    return false;
                }
            }

            // 只处理需要内容的文件类型
            const validTypes = ['modified', 'added', 'deleted', 'changed', 'new', 'removed', 'renamed', 'copied'];
            const isValid = validTypes.includes(type);

            if (!isValid) {
                logDebug('批量文件获取', '跳过未知类型的文件', {
                    filePath,
                    type,
                    originalType: diff.statistic?.type,
                    hasStatusOverride: !!fileStatuses
                });
                return false;
            }

            return true;
        });

        // 统计文件类型分布（使用修正后的类型）
        const fileTypeStats = {};
        modifiedFiles.forEach(diff => {
            const filePath = diff.statistic.new_path || diff.statistic.old_path;
            let type = diff.statistic?.type || 'unknown';

            // 尝试从文件状态信息中获取正确的类型
            if (fileStatuses && fileStatuses.length > 0) {
                const matchingStatus = fileStatuses.find(status =>
                    (status.path === filePath) ||
                    (status.newPath === diff.statistic.new_path) ||
                    (status.oldPath === diff.statistic.old_path) ||
                    (status.path === (diff.statistic.new_path || diff.statistic.old_path))
                );

                if (matchingStatus) {
                    type = matchingStatus.status;
                }
            }

            fileTypeStats[type] = (fileTypeStats[type] || 0) + 1;
        });

        logDebug('批量文件获取', '文件过滤完成', {
            totalFiles: filesData.diffs.length,
            filteredFiles: modifiedFiles.length,
            base_sha,
            head_sha,
            fileTypeStats
        });

        // 如果没有需要处理的文件，提前返回
        if (modifiedFiles.length === 0) {
            logDebug('批量文件获取', '没有需要获取内容的文件，提前返回');
            return { baseFiles: [], headFiles: [] };
        }

        // 分别获取base和head版本的文件内容（使用并发控制）
        const allRequests = [];

        modifiedFiles.forEach(diff => {
            const filePath = diff.statistic.new_path || diff.statistic.old_path;
            let type = diff.statistic?.type;
            const newPath = diff.statistic.new_path;
            const oldPath = diff.statistic.old_path;

            // 尝试从传入的文件状态信息中获取正确的变更类型
            if (fileStatuses && fileStatuses.length > 0) {
                const matchingStatus = fileStatuses.find(status =>
                    (status.path === filePath) ||
                    (status.newPath === newPath) ||
                    (status.oldPath === oldPath) ||
                    (status.path === (newPath || oldPath))
                );

                if (matchingStatus) {
                    type = matchingStatus.status;
                    logDebug('批量文件获取', '使用正确的文件状态', {
                        filePath,
                        originalType: diff.statistic?.type,
                        correctedType: type,
                        statusSource: 'fileStatuses'
                    });
                }
            }

            logDebug('批量文件获取', '处理文件请求', {
                filePath,
                type,
                newPath,
                oldPath,
                fileIndex: allRequests.length,
                hasStatusOverride: !!fileStatuses
            });

            // 获取base版本（修改前）的内容
            const shouldGetBase = type !== 'added' && type !== 'new'; // 新增文件没有base版本
            if (shouldGetBase) {
                const baseFilePath = oldPath || newPath;
                if (baseFilePath) {
                    logDebug('批量文件获取', '计划获取base版本', {
                        filePath: baseFilePath,
                        type,
                        reason: '非新增文件需要base版本'
                    });
                    allRequests.push(
                        apiCallWithRetry(
                            () => fetchFileContent({ owner, repo, filePath: baseFilePath, sha: base_sha }, token),
                            `获取base版本: ${baseFilePath}`
                        ).catch(error => {
                            logDebug('批量文件获取', '获取base版本失败', {
                                filePath: baseFilePath,
                                error: error.message,
                                type
                            });
                            return null;
                        })
                    );
                } else {
                    logDebug('批量文件获取', '跳过base版本获取 - 无有效路径', {
                        type,
                        oldPath,
                        newPath
                    });
                }
            } else {
                logDebug('批量文件获取', '跳过base版本获取', {
                    filePath: oldPath || newPath,
                    type,
                    reason: '新增文件无base版本'
                });
            }

            // 获取head版本（修改后）的内容
            const shouldGetHead = type !== 'deleted' && type !== 'removed'; // 删除文件没有head版本
            if (shouldGetHead) {
                const headFilePath = newPath || oldPath;
                if (headFilePath) {
                    logDebug('批量文件获取', '计划获取head版本', {
                        filePath: headFilePath,
                        type,
                        reason: '非删除文件需要head版本'
                    });
                    allRequests.push(
                        apiCallWithRetry(
                            () => fetchFileContent({ owner, repo, filePath: headFilePath, sha: head_sha }, token),
                            `获取head版本: ${headFilePath}`
                        ).catch(error => {
                            logDebug('批量文件获取', '获取head版本失败', {
                                filePath: headFilePath,
                                error: error.message,
                                type
                            });
                            return null;
                        })
                    );
                } else {
                    logDebug('批量文件获取', '跳过head版本获取 - 无有效路径', {
                        type,
                        oldPath,
                        newPath
                    });
                }
            } else {
                logDebug('批量文件获取', '跳过head版本获取', {
                    filePath: newPath || oldPath,
                    type,
                    reason: '删除文件无head版本'
                });
            }
        });

        // 统计请求数量和类型（使用修正后的类型）
        const requestStats = {
            baseRequests: 0,
            headRequests: 0,
            totalRequests: allRequests.length,
            fileTypes: {},
            statusCorrection: 0
        };

        // 重新统计，使用实际的文件状态
        modifiedFiles.forEach(diff => {
            const originalType = diff.statistic?.type;
            let actualType = originalType;

            // 如果有状态覆盖信息，使用修正后的类型
            if (fileStatuses && fileStatuses.length > 0) {
                const filePath = diff.statistic.new_path || diff.statistic.old_path;
                const matchingStatus = fileStatuses.find(status =>
                    (status.path === filePath) ||
                    (status.newPath === diff.statistic.new_path) ||
                    (status.oldPath === diff.statistic.oldPath) ||
                    (status.path === (diff.statistic.new_path || diff.statistic.old_path))
                );

                if (matchingStatus && matchingStatus.status !== originalType) {
                    actualType = matchingStatus.status;
                    requestStats.statusCorrection++;
                }
            }

            requestStats.fileTypes[actualType] = (requestStats.fileTypes[actualType] || 0) + 1;

            if (actualType !== 'added' && actualType !== 'new') {
                requestStats.baseRequests++;
            }
            if (actualType !== 'deleted' && actualType !== 'removed') {
                requestStats.headRequests++;
            }
        });

        logDebug('批量文件获取', '请求统计', {
            ...requestStats,
            maxConcurrent: MAX_CONCURRENT_REQUESTS,
            expectedTotalRequests: requestStats.baseRequests + requestStats.headRequests
        });

        try {
            logDebug('批量文件获取', '开始并发获取文件内容', {
                totalRequests: allRequests.length,
                maxConcurrent: MAX_CONCURRENT_REQUESTS,
                actualVsExpected: allRequests.length
            });

            // 使用分批处理来控制并发数量
            const results = [];
            for (let i = 0; i < allRequests.length; i += MAX_CONCURRENT_REQUESTS) {
                const batch = allRequests.slice(i, i + MAX_CONCURRENT_REQUESTS);
                logDebug('批量文件获取', `处理第 ${Math.floor(i / MAX_CONCURRENT_REQUESTS) + 1} 批请求`, {
                    batchSize: batch.length,
                    batchIndex: Math.floor(i / MAX_CONCURRENT_REQUESTS) + 1,
                    totalBatches: Math.ceil(allRequests.length / MAX_CONCURRENT_REQUESTS)
                });

                const batchResults = await Promise.all(batch);
                results.push(...batchResults);

                // 批次之间添加额外延迟，避免触发限流
                if (i + MAX_CONCURRENT_REQUESTS < allRequests.length) {
                    logDebug('批量文件获取', '批次间延迟，避免触发限流');
                    await new Promise(resolve => setTimeout(resolve, API_RATE_LIMIT_DELAY));
                }
            }

            const successfulFiles = results.filter(file => file !== null);
            const failedFiles = results.filter(file => file === null).length;

            logDebug('批量文件获取', '文件内容获取完成', {
                totalRequests: allRequests.length,
                successfulFiles: successfulFiles.length,
                failedFiles: failedFiles,
                totalContentBytes: successfulFiles.reduce((total, file) => total + (file?.size || 0), 0),
                anySuccess: successfulFiles.length > 0
            });

            if (successfulFiles.length === 0) {
                throw new Error(`所有文件获取均失败 (${failedFiles}/${allRequests.length})，请检查 Access Token 权限或网络连接`);
            }

            // 分离 base 和 head 文件（为了保持与原有接口的兼容性）
            const baseFiles = successfulFiles.filter(file => {
                // 简单的启发式方法：根据文件路径和内容判断是否为base版本
                // 这里可能需要根据实际需求调整
                return true; // 暂时返回所有文件，后续可以根据需要优化
            });

            const headFiles = successfulFiles;

            return {
                baseFiles: baseFiles,
                headFiles: headFiles
            };

        } catch (error) {
            logDebug('批量文件获取', '批量获取过程中发生错误', {
                error: error.message,
                stack: error.stack,
                totalRequests: allRequests.length
            });
            throw new Error(`批量获取文件内容失败: ${error.message}`);
        }
    }

    // 新增：格式化完整文件内容用于prompt
    function formatCompleteFilesContent(baseFiles, headFiles, maxFiles = 3, maxFileSize = 3000) {
        if (!baseFiles.length && !headFiles.length) {
            return '--- 完整文件对比 ---\n（无法获取完整的文件内容，将仅基于diff片段进行分析）\n';
        }

        // 创建文件映射以便快速查找
        const baseFileMap = new Map(baseFiles.map(file => [file.path, file]));
        const headFileMap = new Map(headFiles.map(file => [file.path, file]));

        // 获取所有修改过的文件路径
        const allFilePaths = new Set([
            ...baseFiles.map(file => file.path),
            ...headFiles.map(file => file.path)
        ]);

        let content = '--- 完整文件对比 ---\n';
        content += `注意：以下展示了修改前后的完整文件内容，便于深度分析代码逻辑和上下文。\n\n`;

        let fileCount = 0;
        for (const filePath of allFilePaths) {
            if (fileCount >= maxFiles) {
                content += `\n（还有 ${allFilePaths.size - fileCount} 个文件未显示，基于token限制）\n`;
                break;
            }

            const baseFile = baseFileMap.get(filePath);
            const headFile = headFileMap.get(filePath);

            content += `## 📄 ${filePath}\n\n`;

            if (baseFile && headFile) {
                // 修改的文件：显示前后对比
                content += `### 🔧 修改前 (base: ${baseFile.sha.slice(0, 7)})\n`;
                if (baseFile.content.length > maxFileSize) {
                    content += `\`\`\`\n${baseFile.content.slice(0, maxFileSize)}\n... (文件过大，已截断)\n\`\`\`\n\n`;
                } else {
                    content += `\`\`\`\n${baseFile.content}\n\`\`\`\n\n`;
                }

                content += `### ✨ 修改后 (head: ${headFile.sha.slice(0, 7)})\n`;
                if (headFile.content.length > maxFileSize) {
                    content += `\`\`\`\n${headFile.content.slice(0, maxFileSize)}\n... (文件过大，已截断)\n\`\`\`\n\n`;
                } else {
                    content += `\`\`\`\n${headFile.content}\n\`\`\`\n\n`;
                }
            } else if (headFile) {
                // 新增的文件
                content += `### ➕ 新增文件 (head: ${headFile.sha.slice(0, 7)})\n`;
                if (headFile.content.length > maxFileSize) {
                    content += `\`\`\`\n${headFile.content.slice(0, maxFileSize)}\n... (文件过大，已截断)\n\`\`\`\n\n`;
                } else {
                    content += `\`\`\`\n${headFile.content}\n\`\`\`\n\n`;
                }
            } else if (baseFile) {
                // 删除的文件
                content += `### ➖ 删除文件 (base: ${baseFile.sha.slice(0, 7)})\n`;
                if (baseFile.content.length > maxFileSize) {
                    content += `\`\`\`\n${baseFile.content.slice(0, maxFileSize)}\n... (文件过大，已截断)\n\`\`\`\n\n`;
                } else {
                    content += `\`\`\`\n${baseFile.content}\n\`\`\`\n\n`;
                }
            }

            fileCount++;
        }

        return content;
    }

    function fetchIssueDetails({ owner, repo, number }, token) {
        return new Promise((resolve, reject) => {
            const apiUrl = `https://api.gitcode.com/api/v5/repos/${owner}/${repo}/issues/${number}?access_token=${token}`;
            GM_xmlhttpRequest({ method: 'GET', url: apiUrl, headers: { 'Accept': 'application/json' },
                onload: res => (res.status >= 200 && res.status < 300) ? resolve(JSON.parse(res.responseText)) : reject(`获取 Issue #${number} 失败: ${res.statusText}`),
                onerror: err => reject(`获取 Issue #${number} 时网络错误: ${err}`) });
        });
    }

    function fetchPRDiff({ owner, repo, prId }) {
        return new Promise((resolve, reject) => {
            const diffUrl = `https://gitcode.com/${owner}/${repo}/pull/${prId}.diff`;
            GM_xmlhttpRequest({ method: 'GET', url: diffUrl, headers: { 'Accept': 'text/plain' },
                onload: res => (res.status >= 200 && res.status < 300) ? resolve(res.responseText) : reject(`获取 PR diff 失败: ${res.statusText}`),
                onerror: err => reject(`获取 PR diff 时网络错误: ${err}`) });
        });
    }

    function detectLanguage(filePath) {
        const fileName = (filePath || '').split('/').pop() || '';
        const lowerFileName = fileName.toLowerCase();
        const extension = lowerFileName.includes('.') ? lowerFileName.split('.').pop() : '';
        const languageMap = {
            js: 'JavaScript', jsx: 'JavaScript', ts: 'TypeScript', tsx: 'TypeScript',
            mjs: 'JavaScript', cjs: 'JavaScript',
            json: 'JSON', yml: 'YAML', yaml: 'YAML',
            css: 'CSS', scss: 'SCSS', less: 'Less',
            html: 'HTML', htm: 'HTML', vue: 'Vue', svelte: 'Svelte', astro: 'Astro',
            md: 'Markdown', markdown: 'Markdown',
            go: 'Go', py: 'Python', rb: 'Ruby', rs: 'Rust', java: 'Java', kt: 'Kotlin',
            swift: 'Swift', php: 'PHP', cs: 'C#', cpp: 'C++', cxx: 'C++', c: 'C', h: 'C/C++ Header',
            hpp: 'C++ Header', hs: 'Haskell', scala: 'Scala', sh: 'Shell', bash: 'Shell', zsh: 'Shell',
            ps1: 'PowerShell', sql: 'SQL', xml: 'XML', ini: 'INI', cfg: 'Config', toml: 'TOML',
            gradle: 'Gradle', properties: 'Properties', dockerfile: 'Dockerfile',
            makefile: 'Makefile'
        };
        if (lowerFileName === 'dockerfile') return 'Dockerfile';
        if (lowerFileName === 'makefile') return 'Makefile';
        if (!extension) return 'Unknown';
        return languageMap[extension] || 'Unknown';
    }

    function analyzeDiff(diffText) {
        const files = [];
        const lines = diffText.split('\n');
        let currentFile = null;
        let currentHunk = null;
        let currentOldLine = 0;
        let currentNewLine = 0;
        const hunkHeaderRegex = /^@@ -([0-9]+)(?:,([0-9]+))? \+([0-9]+)(?:,([0-9]+))? @@/;

        const pushCurrent = () => {
            if (!currentFile) return;
            const displayPath = currentFile.status === 'deleted' ? currentFile.oldPath : currentFile.newPath;
            currentFile.path = displayPath || currentFile.oldPath || currentFile.newPath;
            currentFile.language = detectLanguage(currentFile.path);
            files.push(currentFile);
            currentFile = null;
            currentHunk = null;
        };

        lines.forEach(line => {
            if (line.startsWith('diff --git')) {
                pushCurrent();
                const match = line.match(/^diff --git a\/(.*?) b\/(.*)$/);
                currentFile = {
                    oldPath: match ? match[1] : '',
                    newPath: match ? match[2] : '',
                    status: 'modified',
                    additions: 0,
                    deletions: 0,
                    language: 'Unknown',
                    hunks: []
                };
                currentHunk = null;
                currentOldLine = 0;
                currentNewLine = 0;
            } else if (!currentFile) {
                return;
            } else if (line.startsWith('new file mode')) {
                currentFile.status = 'added';
            } else if (line.startsWith('deleted file mode')) {
                currentFile.status = 'deleted';
            } else if (line.startsWith('rename from ')) {
                currentFile.status = 'renamed';
                currentFile.oldPath = line.replace('rename from ', '').trim();
            } else if (line.startsWith('rename to ')) {
                currentFile.status = 'renamed';
                currentFile.newPath = line.replace('rename to ', '').trim();
            } else if (line.startsWith('--- ')) {
                if (line.includes('/dev/null')) {
                    currentFile.status = 'added';
                }
            } else if (line.startsWith('+++ ')) {
                if (line.includes('/dev/null')) {
                    currentFile.status = 'deleted';
                }
            } else if (line.startsWith('@@ ')) {
                const match = line.match(hunkHeaderRegex);
                if (match) {
                    const oldStart = parseInt(match[1], 10);
                    const newStart = parseInt(match[3], 10);
                    currentOldLine = isNaN(oldStart) ? 0 : oldStart;
                    currentNewLine = isNaN(newStart) ? 0 : newStart;
                }
                currentHunk = { header: line, lines: [] };
                currentFile.hunks.push(currentHunk);
            } else if (line.startsWith('@@')) {
                // Handles edge case with no space after @@
            } else if (line.startsWith('+') && !line.startsWith('+++')) {
                currentFile.additions += 1;
                if (currentHunk) {
                    currentHunk.lines.push({
                        type: '+',
                        text: line.slice(1),
                        newLine: currentNewLine,
                        oldLine: null
                    });
                }
                if (currentNewLine !== null && currentNewLine !== undefined) currentNewLine += 1;
            } else if (line.startsWith('-') && !line.startsWith('---')) {
                currentFile.deletions += 1;
                if (currentHunk) {
                    currentHunk.lines.push({
                        type: '-',
                        text: line.slice(1),
                        newLine: null,
                        oldLine: currentOldLine
                    });
                }
                if (currentOldLine !== null && currentOldLine !== undefined) currentOldLine += 1;
            } else if (line.startsWith(' ')) {
                if (currentHunk) {
                    currentHunk.lines.push({
                        type: ' ',
                        text: line.slice(1),
                        newLine: currentNewLine,
                        oldLine: currentOldLine
                    });
                }
                if (currentOldLine !== null && currentOldLine !== undefined) currentOldLine += 1;
                if (currentNewLine !== null && currentNewLine !== undefined) currentNewLine += 1;
            } else if (line.startsWith('\\')) {
                // Skip "\ No newline at end of file"
            }
        });
        pushCurrent();
        return files;
    }

    function getDirectoryFromPath(filePath) {
        if (!filePath) return '';
        const idx = filePath.lastIndexOf('/');
        return idx >= 0 ? filePath.slice(0, idx) : '';
    }

    function getBasenameWithoutExt(filePath) {
        if (!filePath) return '';
        const name = filePath.split('/').pop() || filePath;
        const dotIndex = name.lastIndexOf('.');
        return dotIndex > 0 ? name.slice(0, dotIndex) : name;
    }

    function groupDiffEntries(fileSummaries) {
        const headerExts = new Set(['h', 'hpp', 'hh', 'hxx', 'h++', 'h.inl', 'inc']);
        const sourceExts = new Set(['c', 'cc', 'cpp', 'cxx', 'c++', 'm', 'mm', 'C', 'CPP']);
        const buildExts = new Set(['cmake', 'mk', 'makefile', 'am', 'in', 'gn', 'gni']);
        const configExts = new Set(['conf', 'cfg', 'ini', 'toml', 'yaml', 'yml']);
        const tsExts = new Set(['ts', 'tsx', 'ets']); // TypeScript/ETS文件

        // 按语言类型分离文件
        const cppFiles = [];
        const tsFiles = [];
        const otherFiles = [];

        fileSummaries.forEach(file => {
            const ext = (file.path && file.path.includes('.')) ? file.path.split('.').pop().toLowerCase() : '';

            if (headerExts.has(ext) || sourceExts.has(ext) || buildExts.has(ext)) {
                cppFiles.push(file);
            } else if (tsExts.has(ext)) {
                tsFiles.push(file);
            } else {
                otherFiles.push(file);
            }
        });

        const groups = [];

        // 分组1: C++ + 构建文件
        if (cppFiles.length > 0) {
            const cppGroup = {
                id: 'cpp-and-build',
                title: `C++系统代码与构建文件 (${cppFiles.length}个文件)`,
                files: cppFiles,
                additions: cppFiles.reduce((sum, file) => sum + file.additions, 0),
                deletions: cppFiles.reduce((sum, file) => sum + file.deletions, 0),
                languages: ['C++', 'Build'],
                statusCounts: {},
                isCppSystem: true,
                reviewType: 'cpp' // 标识使用C++模板
            };

            // 统计状态分布
            cppFiles.forEach(file => {
                cppGroup.statusCounts[file.status] = (cppGroup.statusCounts[file.status] || 0) + 1;
            });

            groups.push(cppGroup);
        }

        // 分组2: TypeScript/ETS文件
        if (tsFiles.length > 0) {
            const tsGroup = {
                id: 'typescript',
                title: `TypeScript/ETS代码 (${tsFiles.length}个文件)`,
                files: tsFiles,
                additions: tsFiles.reduce((sum, file) => sum + file.additions, 0),
                deletions: tsFiles.reduce((sum, file) => sum + file.deletions, 0),
                languages: ['TypeScript', 'ETS'],
                statusCounts: {},
                isCppSystem: false,
                reviewType: 'ts' // 标识使用通用模板
            };

            // 统计状态分布
            tsFiles.forEach(file => {
                tsGroup.statusCounts[file.status] = (tsGroup.statusCounts[file.status] || 0) + 1;
            });

            groups.push(tsGroup);
        }

        // 分组3: 其他文件（如果有）
        if (otherFiles.length > 0) {
            const otherGroup = {
                id: 'others',
                title: `其他文件 (${otherFiles.length}个文件)`,
                files: otherFiles,
                additions: otherFiles.reduce((sum, file) => sum + file.additions, 0),
                deletions: otherFiles.reduce((sum, file) => sum + file.deletions, 0),
                languages: [...new Set(otherFiles.map(f => f.language || 'Unknown'))],
                statusCounts: {},
                isCppSystem: false,
                reviewType: 'general'
            };

            // 统计状态分布
            otherFiles.forEach(file => {
                otherGroup.statusCounts[file.status] = (otherGroup.statusCounts[file.status] || 0) + 1;
            });

            groups.push(otherGroup);
        }

        return groups;
    }

    // 筛选特定组的文件内容
    function filterFilesContentForGroup(completeFilesContent, groupFiles) {
        if (!completeFilesContent || !groupFiles.length) return '';

        const groupFilePaths = new Set(groupFiles.map(file => file.path));
        const lines = completeFilesContent.split('\n');
        const filteredLines = [];
        let currentFile = null;
        let includeCurrentFile = false;

        for (const line of lines) {
            if (line.startsWith('--- ') || line.startsWith('+++ ')) {
                currentFile = line.substring(4).trim();
                includeCurrentFile = groupFilePaths.has(currentFile);
            }

            if (includeCurrentFile || line.startsWith('--- ') || line.startsWith('+++ ')) {
                filteredLines.push(line);
            }
        }

        return filteredLines.join('\n');
    }

    // 为单个组调用LLM分析
    async function callLLMForGroup(baseUrl, apiKey, modelName, prompt, resultContainer, reviewType) {
        return new Promise((resolve) => {
            GM_xmlhttpRequest({
                method: 'POST',
                url: baseUrl,
                headers: {
                    'Content-Type': 'application/json',
                    'Authorization': `Bearer ${apiKey}`
                },
                data: JSON.stringify({
                    model: modelName,
                    messages: [{ role: 'user', content: prompt }],
                    stream: false
                }),
                onload: function(response) {
                    if (response.status < 200 || response.status >= 300) {
                        resultContainer.innerHTML = `
                            <div class="llm-progress-view">
                                <h4 style="color: #dc3545;">⚠️ ${reviewType === 'cpp' ? 'C++系统审核' : 'TypeScript审核'}失败</h4>
                                <p>HTTP ${response.status}</p>
                                <pre style="white-space: pre-wrap; background-color: #eee; padding: 10px; border-radius: 5px; text-align: left;">${response.responseText}</pre>
                            </div>
                        `;
                        resolve();
                        return;
                    }

                    try {
                        const data = JSON.parse(response.responseText);
                        const reviewContent = data.choices[0].message.content;
                        const cleanMessage = reviewContent.replace(/```json/g, '').replace(/```/g, '').trim();
                        const jsonData = JSON.parse(cleanMessage);
                        renderSecondaryLLMResult(resultContainer, jsonData);
                    } catch (error) {
                        console.error('LLM parse error:', error);
                        resultContainer.innerHTML = `
                            <div class="llm-progress-view">
                                <h4 style="color: #dc3545;">⚠️ ${reviewType === 'cpp' ? 'C++系统审核' : 'TypeScript审核'}结果解析失败</h4>
                                <p>${error.message}</p>
                                <pre style="white-space: pre-wrap; background-color: #eee; padding: 10px; border-radius: 5px; text-align: left;">${response.responseText}</pre>
                            </div>
                        `;
                    }
                    resolve();
                },
                onerror: function(err) {
                    resultContainer.innerHTML = `
                        <div class="llm-progress-view">
                            <h4 style="color: #dc3545;">⚠️ ${reviewType === 'cpp' ? 'C++系统审核' : 'TypeScript审核'}请求失败</h4>
                            <p>${JSON.stringify(err)}</p>
                        </div>
                    `;
                    resolve();
                }
            });
        });
    }

    // 检测C++子系统类型
    function detectCppSubsystem(filePath, dir) {
        const path = (filePath || '').toLowerCase();
        const dirPath = (dir || '').toLowerCase();

        // 操作系统内核相关
        if (path.includes('kernel') || path.includes('driver') || path.includes('syscall') ||
            dirPath.includes('kernel') || dirPath.includes('driver') || dirPath.includes('arch') ||
            path.includes('interrupt') || path.includes('mm/') || path.includes('fs/')) {
            return '内核系统';
        }

        // 内存管理
        if (path.includes('memory') || path.includes('alloc') || path.includes('gc') ||
            path.includes('pool') || path.includes('heap') || path.includes('stack')) {
            return '内存管理';
        }

        // 并发与同步
        if (path.includes('thread') || path.includes('mutex') || path.includes('atomic') ||
            path.includes('lock') || path.includes('sync') || path.includes('concurrent') ||
            path.includes('semaphore') || path.includes('condition')) {
            return '并发同步';
        }

        // 网络系统
        if (path.includes('net') || path.includes('socket') || path.includes('protocol') ||
            path.includes('tcp') || path.includes('udp') || path.includes('http')) {
            return '网络系统';
        }

        // 文件系统
        if (path.includes('file') || path.includes('fs') || path.includes('io') ||
            path.includes('storage') || path.includes('disk')) {
            return '文件系统';
        }

        // 设备管理
        if (path.includes('device') || path.includes('hw') || path.includes('pci') ||
            path.includes('usb') || path.includes('gpio')) {
            return '设备管理';
        }

        // 安全系统
        if (path.includes('security') || path.includes('crypto') || path.includes('auth') ||
            path.includes('cipher') || path.includes('hash') || path.includes('ssl')) {
            return '安全系统';
        }

        // 系统调用/接口
        if (path.includes('syscall') || path.includes('api') || path.includes('interface') ||
            path.includes('abi') || path.includes('ipc')) {
            return '系统接口';
        }

        // 基础库/框架
        if (path.includes('base') || path.includes('core') || path.includes('foundation') ||
            path.includes('framework') || path.includes('common') || path.includes('util')) {
            return '基础框架';
        }

        return '通用模块';
    }

    function buildFileSnippet(file, maxLines = 80) {
        if (!file.hunks || file.hunks.length === 0) return '';
        const snippetLines = [];
        outer: for (const hunk of file.hunks) {
            snippetLines.push(`    ${hunk.header}`);
            if (!hunk.lines) continue;
            for (const line of hunk.lines) {
                if (snippetLines.length >= maxLines) {
                    snippetLines.push('    ... (更多代码片段已省略)');
                    break outer;
                }
                if (line.type === '+') {
                    snippetLines.push(`      + L${line.newLine ?? '?'}: ${line.text}`);
                } else if (line.type === '-') {
                    snippetLines.push(`      - (旧行 ${line.oldLine ?? '?'}) ${line.text}`);
                } else if (line.type === ' ') {
                    const marker = line.newLine != null ? `      · L${line.newLine}:` : '      ·';
                    snippetLines.push(`${marker} ${line.text}`);
                }
            }
        }
        return snippetLines.join('\n');
    }

    function summarizeStatusCounts(statusCounts) {
        return Object.entries(statusCounts).map(([status, count]) => `${getStatusLabel(status)} ${count}`).join('，');
    }

    function setSecondaryProgress(container, text) {
        container.innerHTML = `<div class="llm-progress-view"><div class="llm-spinner"></div><p>${text}</p></div>`;
    }

    function getModalSections(modalBody) {
        let primary = modalBody.querySelector('#primary-review-content');
        let secondary = modalBody.querySelector('#secondary-review-wrapper');
        if (!primary || !secondary) {
            modalBody.innerHTML = `
                <div id="primary-review-content"></div>
                <div id="secondary-review-wrapper" style="margin-top: 20px;"></div>
            `;
            primary = modalBody.querySelector('#primary-review-content');
            secondary = modalBody.querySelector('#secondary-review-wrapper');
        }
        return { primary, secondary };
    }

    // HTML转义函数，防止特殊字符破坏HTML结构
    function escapeHtml(text) {
        if (!text || typeof text !== 'string') return text;
        return text
            .replace(/&/g, '&amp;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/"/g, '&quot;')
            .replace(/'/g, '&#39;');
    }

    function getRiskColor(riskLevel) {
        if (!riskLevel) return '#6c757d';
        const lower = riskLevel.toLowerCase();

        // 英文风险级别
        if (lower.includes('high')) return '#dc3545';
        if (lower.includes('medium')) return '#ffc107';
        if (lower.includes('low') || lower.includes('ok')) return '#28a745';
        if (lower.includes('critical')) return '#dc3545';

        // 中文风险级别 (C++模板)
        if (riskLevel.includes('灾难性')) return '#dc3545';
        if (riskLevel.includes('高')) return '#fd7e14';
        if (riskLevel.includes('中')) return '#ffc107';
        if (riskLevel.includes('低')) return '#28a745';

        return '#6c757d';
    }

    function getCategoryIcon(category) {
        const categoryIcons = {
            // 英文类别图标
            'Security': '🔒',
            'Reliability': '🛡️',
            'Architecture': '🏗️',
            'Quality': '✨',
            'Memory': '💾',
            'Concurrency': '🔄',
            'System': '⚙️',
            'Performance': '⚡',
            // 中文类别图标 (C++模板)
            '安全性': '🔒',
            '可靠性': '🛡️',
            '架构': '🏗️',
            '质量': '✨'
        };
        return categoryIcons[category] || '📋';
    }

    function getCategoryColor(category) {
        const categoryColors = {
            // 英文类别
            'Security': '#dc3545',
            'Reliability': '#fd7e14',
            'Architecture': '#6f42c1',
            'Quality': '#20c997',
            'Memory': '#e83e8c',
            'Concurrency': '#fd7e14',
            'System': '#6f42c1',
            'Performance': '#ffc107',
            // 中文类别 (C++模板)
            '安全性': '#dc3545',
            '可靠性': '#fd7e14',
            '架构': '#6f42c1',
            '质量': '#20c997'
        };
        return categoryColors[category] || '#6c757d';
    }

    function renderSecondaryLLMResult(container, result) {
        const overallColor = getRiskColor(result.overall_conclusion);
        const groupFeedbackHtml = Array.isArray(result.group_feedback) ? result.group_feedback.map(item => {
            const hasFiles = Array.isArray(item.files_to_focus) && item.files_to_focus.length > 0;
            const filesList = hasFiles
                ? `<ul style="margin: 8px 0 0 16px;">${item.files_to_focus.map(file => `<li><code>${escapeHtml(file)}</code></li>`).join('')}</ul>`
                : '<p style="margin: 8px 0 0 0; color: #dc3545;">⚠️ 模型未列出具体文件，请人工确认。</p>';
            return `
                <div class="llm-feedback-card">
                    <div class="llm-feedback-card-header">
                        <strong>${escapeHtml(item.group_title || '未命名分组')}</strong>
                        <span style="color: ${getRiskColor(item.risk_level)};">${escapeHtml(item.risk_level || '未知')}</span>
                    </div>
                    <p class="llm-feedback-card-comment">${escapeHtml(item.notes || '无具体说明。')}</p>
                    ${filesList}
                </div>
            `;
        }).join('') : '<div class="llm-feedback-card"><p style="margin: 0;">模型未返回具体分组建议。</p></div>';

        const checksHtml = Array.isArray(result.recommended_checks) && result.recommended_checks.length > 0
            ? `<ul style="margin: 0;">${result.recommended_checks.map(item => `<li>${escapeHtml(item)}</li>`).join('')}</ul>`
            : '<p style="margin: 0; color: #6c757d;">未提供额外检查建议。</p>';

        // 处理C++系统代码的关键问题（如果存在）
        const criticalIssuesHtml = Array.isArray(result.critical_issues) && result.critical_issues.length > 0
            ? `<div class="llm-summary-card" style="margin-top: 15px;">
                    <h4 style="margin: 0 0 8px 0; font-size: 15px; color: #dc3545;">🚨 C++系统代码关键问题</h4>
                    <div class="llm-table-container">
                        <table class="llm-findings-table">
                            <thead>
                                <tr>
                                    <th style="width: 140px;">文件</th>
                                    <th style="width: 70px;">行区间</th>
                                    <th style="width: 80px;">严重级别</th>
                                    <th style="width: 90px;">类别</th>
                                    <th style="width: 250px;">问题描述</th>
                                    <th style="width: 250px;">修复建议</th>
                                    <th style="width: 90px;">操作</th>
                                </tr>
                            </thead>
                            <tbody>
                                ${result.critical_issues.map((item, index) => {
                                    // 映射C++模板字段到通用字段
                                    const location = item.code_location || item.file || '未知文件';
                                    // 从 code_location 中分离文件路径和行号
                                    const pathMatch = location.match(/^([^:]+)(?::(L?\d+(?:-L?\d+)?))?$/);
                                    const file = pathMatch ? pathMatch[1] : location;
                                    const lineSpan = pathMatch && pathMatch[2] ? pathMatch[2] : '未知';
                                    const severity = item.risk_level || '未知';
                                    const category = item.dimension || '未分类';
                                    const issue = item.technical_details || item.title || '未描述';
                                    const suggestion = item.fix_suggestion || '未给出建议';

                                    return `<tr>
                                        <td><code>${escapeHtml(file)}</code></td>
                                        <td>${formatLineSpanDisplay(lineSpan)}</td>
                                        <td style="color: ${getRiskColor(severity)}; font-weight: 600; font-size: 14px;">${escapeHtml(severity)}</td>
                                        <td style="color: ${getCategoryColor(category)}; font-weight: 600;">
                                            ${getCategoryIcon(category)} ${escapeHtml(category)}
                                        </td>
                                        <td class="issue-text" style="font-weight: 500;">${escapeHtml(issue)}</td>
                                        <td class="suggestion-text">${escapeHtml(suggestion)}</td>
                                        <td style="text-align: center; vertical-align: middle;">
                                            <button
                                                class="llm-submit-review-btn"
                                                data-index="${index}"
                                                data-file="${escapeHtml(file)}"
                                                data-line="${escapeHtml(lineSpan)}"
                                                data-issue="${escapeHtml(issue)}"
                                                data-suggestion="${escapeHtml(suggestion)}"
                                                data-category="${escapeHtml(category)}"
                                                data-severity="${escapeHtml(severity)}"
                                                style="padding: 6px 10px; font-size: 12px; background-color: #0366d6; color: white; border: none; border-radius: 4px; cursor: pointer; white-space: nowrap; width: 100%;"
                                                title="提交为PR检视意见">
                                                💬 提交意见
                                            </button>
                                        </td>
                                    </tr>`;
                                }).join('')}
                            </tbody>
                        </table>
                    </div>
                </div>`
            : '';

        // 统一处理 findings 和 critical_issues，让C++和普通代码使用相同的显示逻辑
        const findings = Array.isArray(result.findings) ? result.findings : Array.isArray(result.critical_issues) ? result.critical_issues : null;
        const findingsHtml = Array.isArray(findings) ? (findings.length > 0
            ? `<div class="llm-summary-card" style="margin-top: 15px;">
                    <h4 style="margin: 0 0 8px 0; font-size: 15px;">🔍 专业审核发现</h4>
                    <div class="llm-table-container">
                        <table class="llm-findings-table">
                            <thead>
                                <tr>
                                    <th style="width: 140px;">文件</th>
                                    <th style="width: 70px;">行区间</th>
                                    <th style="width: 80px;">严重级别</th>
                                    <th style="width: 90px;">类别</th>
                                    <th style="width: 250px;">问题描述</th>
                                    <th style="width: 250px;">修复建议</th>
                                    <th style="width: 90px;">操作</th>
                                </tr>
                            </thead>
                            <tbody>
                                ${findings.map((item, index) => {
                                    // 处理C++和通用代码的字段映射
                                    const location = item.code_location || item.file || '未知文件';
                                    // 从 code_location 中分离文件路径和行号（C++模板格式）
                                    const pathMatch = location.match(/^([^:]+)(?::(L?\d+(?:-L?\d+)?))?$/);
                                    const file = pathMatch ? pathMatch[1] : location;
                                    const lineSpan = pathMatch && pathMatch[2] ? pathMatch[2] : (item.line_span || '未知');
                                    const severity = item.risk_level || item.severity || '未知';
                                    const category = item.dimension || item.category || '未分类';
                                    const issue = item.technical_details || item.title || item.issue || '未描述';
                                    const suggestion = item.fix_suggestion || item.suggestion || '未给出建议';

                                    return `<tr>
                                        <td><code>${escapeHtml(file)}</code></td>
                                        <td>${formatLineSpanDisplay(lineSpan)}</td>
                                        <td style="color: ${getRiskColor(severity)}; font-weight: 600;">${escapeHtml(severity)}</td>
                                        <td style="color: ${getCategoryColor(category)}; font-weight: 600;">
                                            ${getCategoryIcon(category)} ${escapeHtml(category)}
                                        </td>
                                        <td class="issue-text">${escapeHtml(issue)}</td>
                                        <td class="suggestion-text">${escapeHtml(suggestion)}</td>
                                        <td style="text-align: center; vertical-align: middle;">
                                            <button
                                                class="llm-submit-review-btn"
                                                data-index="${index}"
                                                data-file="${escapeHtml(file)}"
                                                data-line="${escapeHtml(lineSpan)}"
                                                data-issue="${escapeHtml(issue)}"
                                                data-suggestion="${escapeHtml(suggestion)}"
                                                data-category="${escapeHtml(category)}"
                                                data-severity="${escapeHtml(severity)}"
                                                style="padding: 6px 10px; font-size: 12px; background-color: #0366d6; color: white; border: none; border-radius: 4px; cursor: pointer; white-space: nowrap; width: 100%;"
                                                title="提交为PR检视意见">
                                                💬 提交意见
                                            </button>
                                        </td>
                                    </tr>`;
                                }).join('')}
                            </tbody>
                        </table>
                    </div>
                    <div style="margin-top: 10px; font-size: 12px; color: #6c757d;">
                        <p><strong>审核维度说明:</strong></p>
                        <div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; margin-top: 5px;">
                            <div>🔒 <strong>Security</strong>: 安全性相关 (输入验证、权限控制、数据泄露等)</div>
                            <div>🛡️ <strong>Reliability</strong>: 可靠性相关 (错误处理、边界条件、资源管理等)</div>
                            <div>🏗️ <strong>Architecture</strong>: 架构设计相关 (代码结构、设计模式、接口设计等)</div>
                            <div>✨ <strong>Quality</strong>: 代码质量相关 (编码规范、测试覆盖、可读性等)</div>
                            <div>💾 <strong>Memory</strong>: 内存安全相关 (内存泄漏、缓冲区溢出、RAII等)</div>
                            <div>🔄 <strong>Concurrency</strong>: 并发安全相关 (线程安全、死锁、原子操作等)</div>
                            <div>⚙️ <strong>System</strong>: 系统调用相关 (内核接口、系统调用、权限等)</div>
                            <div>⚡ <strong>Performance</strong>: 性能优化相关 (算法效率、资源使用、延迟等)</div>
                        </div>
                    </div>
                </div>`
            : '<div class="llm-summary-card" style="margin-top: 15px; color: #28a745;">✅ 模型未报告新的问题，已显式返回空 findings。</div>')
            : '';

        container.innerHTML = `
            <div class="llm-summary-card">
                <p style="margin: 0 0 10px 0;">
                    <span class="llm-assessment" style="background-color: ${overallColor};">
                        ${escapeHtml(result.overall_conclusion || '未知')}
                    </span>
                </p>
                <p style="margin: 0;"><strong>摘要:</strong> ${escapeHtml(result.summary || '无概要。')}</p>
            </div>
            <div>${groupFeedbackHtml}</div>
            ${findingsHtml}
            <div class="llm-summary-card">
                <p style="margin: 0 0 6px 0;"><strong>建议的补充检查:</strong></p>
                ${checksHtml}
            </div>
        `;

        const wrapper = container.parentElement;
        if (wrapper) {
            lastSecondaryFragment = wrapper.cloneNode(true);
            // 同时更新渐进式缓存
            analysisState.secondaryResult = wrapper.cloneNode(true);
            updateAnalysisStep('analyzing_secondary', analysisState.secondaryResult);
        }

        // 添加提交检视意见按钮的事件监听器（使用事件委托确保动态内容也有效）
        const buttons = container.querySelectorAll('.llm-submit-review-btn');
        logDebug('事件监听器', `找到 ${buttons.length} 个提交按钮，开始绑定事件`, {
            buttonCount: buttons.length,
            containerId: container.id || 'unknown'
        });

        buttons.forEach((btn, index) => {
            try {
                // 移除可能存在的旧事件监听器
                btn.removeEventListener('click', handleSubmitReview);
                // 添加新的事件监听器
                btn.addEventListener('click', handleSubmitReview);
                logDebug('事件监听器', `成功绑定第 ${index + 1} 个按钮`, {
                    buttonText: btn.textContent,
                    buttonData: {
                        file: btn.dataset.file,
                        line: btn.dataset.line,
                        category: btn.dataset.category
                    }
                });
            } catch (error) {
                logDebug('事件监听器', `绑定第 ${index + 1} 个按钮失败`, {
                    error: error.message,
                    button: btn.outerHTML.slice(0, 100)
                });
            }
        });

    }

    // 设置全局事件委托，确保所有动态生成的提交按钮都能正常工作
    function setupGlobalEventDelegation() {
        logDebug('事件委托', '开始设置全局事件委托');

        // 使用 document 作为事件委托的根节点
        document.addEventListener('click', function(event) {
            const button = event.target.closest('.llm-submit-review-btn');
            if (button) {
                event.preventDefault();
                event.stopPropagation();

                logDebug('事件委托', '捕获到提交按钮点击', {
                    buttonText: button.textContent,
                    buttonData: {
                        file: button.dataset.file,
                        line: button.dataset.line,
                        category: button.dataset.category,
                        severity: button.dataset.severity
                    }
                });

                // 调用处理函数
                try {
                    handleSubmitReview.call(button, event);
                } catch (error) {
                    logDebug('事件委托', '调用 handleSubmitReview 时发生错误', {
                        error: error.message,
                        errorStack: error.stack
                    });
                    // 重新抛出错误，让 handleSubmitReview 自己处理
                    throw error;
                }
            }
              });

        // 定期检查页面上的提交按钮，用于调试
        if (debugMode) {
            setInterval(() => {
                const buttons = document.querySelectorAll('.llm-submit-review-btn');
                if (buttons.length > 0) {
                    logDebug('事件委托', '定期检查 - 发现提交按钮', {
                        buttonCount: buttons.length,
                        buttonDetails: Array.from(buttons).map(btn => ({
                            text: btn.textContent,
                            disabled: btn.disabled,
                            hasData: {
                                file: !!btn.dataset.file,
                                line: !!btn.dataset.line,
                                category: !!btn.dataset.category
                            }
                        }))
                    });
                }
            }, 5000); // 每5秒检查一次
        }

        logDebug('事件委托', '全局事件委托设置完成');
    }

    // 全局提交状态跟踪
    let submittingButtons = new Set();

    // 页面卸载时清理提交状态
    window.addEventListener('beforeunload', () => {
        submittingButtons.clear();
    });

    // 监听页面变化（SPA应用）
    let lastUrl = window.location.href;
    const checkUrlChange = () => {
        if (window.location.href !== lastUrl) {
            logDebug('页面变化', '检测到URL变化，清理提交状态', {
                from: lastUrl,
                to: window.location.href
            });
            submittingButtons.clear();
            lastUrl = window.location.href;
        }
    };

    // 定期检查URL变化
    setInterval(checkUrlChange, 1000);

    async function handleSubmitReview(event) {
        try {
            logDebug('提交评论', 'handleSubmitReview 函数被调用', {
                eventType: event.type,
                timestamp: new Date().toISOString()
            });

            // 获取正确的按钮引用（支持事件委托）
            let button = event.target;
            if (!button.classList.contains('llm-submit-review-btn')) {
                button = event.target.closest('.llm-submit-review-btn');
            }

            if (!button) {
                logDebug('提交评论', '无法找到按钮元素', {
                    eventTarget: event.target.tagName,
                    eventTargetClass: event.target.className,
                    eventTargetHTML: event.target.outerHTML?.slice(0, 100)
                });
                return;
            }

            // 增强的防重复提交机制
            const buttonId = button.dataset.index || `${button.dataset.file}-${button.dataset.line}`;

            // 检查按钮是否已经在提交中
            if (submittingButtons.has(buttonId)) {
                logDebug('提交评论', '检测到按钮正在提交中，忽略重复点击', {
                    buttonId,
                    buttonText: button.textContent
                });
                return;
            }

            // 检查按钮是否已被禁用
            if (button.disabled && button.textContent.includes('已提交')) {
                logDebug('提交评论', '检测到按钮已提交，忽略点击', {
                    buttonId,
                    buttonText: button.textContent,
                    buttonDisabled: button.disabled
                });
                return;
            }

            logDebug('提交评论', '开始处理提交', {
                buttonId,
                buttonText: button.textContent,
                buttonDisabled: button.disabled,
                buttonClasses: button.className
            });
        const data = {
            file: button.dataset.file,
            lineSpan: button.dataset.line,
            issue: button.dataset.issue,
            suggestion: button.dataset.suggestion,
            category: button.dataset.category,
            severity: button.dataset.severity
        };

        // 检查是否已经提交过相同的意见
        const reviewKey = `${data.file}:${data.lineSpan}:${data.category}:${data.issue.slice(0, 50)}`;
        if (submittedReviews.has(reviewKey)) {
            logDebug('提交评论', '检测到重复提交', { reviewKey });
            showInfoToast('此意见已经提交过了', '无需重复提交相同的评论');
            return;
        }

        logDebug('提交评论', '提取到按钮数据', data);

        // 验证必要数据
        if (!data.file || !data.lineSpan) {
            const errorMsg = '❌ 文件路径或行号信息不完整，无法提交检视意见';
            logDebug('提交评论', '数据验证失败', {
                hasFile: !!data.file,
                hasLineSpan: !!data.lineSpan,
                allData: data
            });
            alert(errorMsg);
            return;
        }

        // 获取配置和仓库信息
        const config = await getConfig();
        const repoInfo = extractRepoInfo();

        if (!config || !config.gitcodeToken) {
            alert('❌ 缺少GitCode配置，请先配置GitCode Access Token');
            return;
        }

        if (!repoInfo) {
            alert('❌ 无法获取仓库信息，请确保在PR页面操作');
            return;
        }

        // 解析行号（使用基础解析）
        logDebug('提交评论', '开始解析行号', {
            lineSpan: data.lineSpan,
            issue: data.issue
        });

        const parsedPosition = parseLinePosition(data.lineSpan);
        logDebug('提交评论', '行号解析完成', {
            originalLineSpan: data.lineSpan,
            parsedPosition
        });

        if (!parsedPosition || parsedPosition.position === null) {
            showErrorToast('行号解析失败', `无法解析行号信息: "${data.lineSpan}"`, [
                { text: '重试', action: () => handleSubmitReview(event) },
                { text: '查看日志', action: () => { setDebugMode(true); showDebugPanel(); } }
            ]);
            return;
        }

        // 构建评论内容
        const commentBody = buildCommentBody(data, parsedPosition);

        // 立即将按钮标记为提交中状态
        submittingButtons.add(buttonId);

        // 禁用按钮，显示加载状态
        const originalText = button.textContent;
        button.disabled = true;
        button.textContent = '🔄 提交中...';
        button.style.backgroundColor = '#6c757d';

        try {
            await submitPRComment(repoInfo, config.gitcodeToken, data.file, parsedPosition.position, commentBody);

            // 创建唯一标识符用于跟踪已提交的意见
            const reviewKey = `${data.file}:${data.lineSpan}:${data.category}:${data.issue.slice(0, 50)}`;
            submittedReviews.add(reviewKey);

            // 成功反馈 - 保持已提交状态更长时间
            button.textContent = '✅ 已提交';
            button.style.backgroundColor = '#28a745';
            button.disabled = true;

            // 在按钮上添加已提交的标记
            button.title = '此意见已成功提交到PR';

            // 可选：10秒后改变为不可点击但显示已提交的状态
            setTimeout(() => {
                button.textContent = '📋 已提交';
                button.style.backgroundColor = '#6c757d';
                button.title = '此意见已提交，无需重复提交';
            }, 10000);

        } catch (error) {
            // 清理提交状态
            submittingButtons.delete(buttonId);

            // 错误反馈
            logDebug('提交评论', '提交失败', {
                error: error.message,
                errorStack: error.stack
            });

            button.textContent = '❌ 失败';
            button.style.backgroundColor = '#dc3545';

            setTimeout(() => {
                button.textContent = originalText;
                button.disabled = false;
                button.style.backgroundColor = '#0366d6';
            }, 3000);

            // 分析错误类型并提供相应的解决方案
            let title = '提交失败';
            let message = error.message;
            let actions = [];

            if (error.message.includes('400') || error.message.includes('position')) {
                title = '400错误 - 代码位置无效';
                message = `代码位置 "${data.lineSpan}" 可能超出了本次PR的变更范围。这通常发生在LLM返回了过大的代码范围时。`;
                actions = [
                    { text: '重新分析PR', action: () => { runAnalysis(); } },
                    { text: '查看日志', action: () => { setDebugMode(true); showDebugPanel(); } },
                    { text: '手动提交', action: () => { window.open(window.location.href, '_blank'); } }
                ];
            } else if (error.message.includes('401') || error.message.includes('403')) {
                title = '认证失败';
                message = 'GitCode Access Token可能已过期或权限不足。';
                actions = [
                    { text: '重新配置', action: () => { openConfigModal(); } },
                    { text: '查看日志', action: () => { setDebugMode(true); showDebugPanel(); } }
                ];
            } else if (error.message.includes('网络') || error.message.includes('timeout')) {
                title = '网络错误';
                message = '网络连接不稳定，请检查网络后重试。';
                actions = [
                    { text: '重试', action: () => { handleSubmitReview(event); } },
                    { text: '查看日志', action: () => { setDebugMode(true); showDebugPanel(); } }
                ];
            } else {
                actions = [
                    { text: '重试', action: () => { handleSubmitReview(event); } },
                    { text: '查看日志', action: () => { setDebugMode(true); showDebugPanel(); } },
                    { text: '手动提交', action: () => { window.open(window.location.href, '_blank'); } }
                ];
            }

            showErrorToast(title, message, actions);
        }
        } catch (error) {
            // 清理提交状态
            submittingButtons.delete(buttonId);

            // 捕获所有未预期的错误
            logDebug('提交评论', 'handleSubmitReview 函数发生未预期错误', {
                error: error.message,
                errorStack: error.stack,
                timestamp: new Date().toISOString()
            });

            // 显示用户友好的错误信息
            showErrorToast('提交评论失败', `发生了未预期的错误: ${error.message}`, [
                { text: '查看日志', action: () => { setDebugMode(true); showDebugPanel(); } },
                { text: '重试', action: () => { handleSubmitReview(event); } }
            ]);
        }
    }

    function parseLinePosition(lineSpan) {
        // 支持格式: "L15", "L15-L27", "42", "L15-L27,L35" 等
        if (!lineSpan) return { position: 1, hasMultipleRanges: false, ranges: [] };

        logDebug('行号解析', '开始解析行号', { lineSpan });

        // 处理描述性范围的智能回退策略
        const descriptivePatterns = [
            /整个文件/i,
            /全文件/i,
            /文件整体/i,
            /函数模块/i,
            /模块/i,
            /整个函数/i,
            /函数整体/i,
            /类/i,
            /整个类/i,
            /方法/i,
            /整个方法/i
        ];

        // 检查是否为描述性范围，如果是则返回第1行
        for (const pattern of descriptivePatterns) {
            if (pattern.test(lineSpan)) {
                logDebug('行号解析', '检测到描述性范围，使用第1行作为回退', {
                    original: lineSpan,
                    matchedPattern: pattern.source,
                    fallbackPosition: 1
                });
                return { position: 1, hasMultipleRanges: false, ranges: [] };
            }
        }

        // 新的解析逻辑：解析所有区间
        const ranges = [];
        const cleanedLineSpan = lineSpan.replace(/\s+/g, ''); // 移除空格

        // 按逗号分割多个区间
        const rangeStrings = cleanedLineSpan.split(',');

        for (const rangeString of rangeStrings) {
            // 尝试匹配范围格式：L15-L27, L15-L27, 15-27 等
            const rangeMatch = rangeString.match(/L?(\d+)\s*[-~]\s*L?(\d+)/);
            if (rangeMatch) {
                const start = parseInt(rangeMatch[1], 10);
                const end = parseInt(rangeMatch[2], 10);

                if (start > 0 && end > 0 && end >= start) {
                    ranges.push({ start, end, type: 'range' });
                    continue;
                }
            }

            // 尝试匹配单行格式：L15, 15 等
            const singleMatch = rangeString.match(/L?(\d+)/);
            if (singleMatch) {
                const line = parseInt(singleMatch[1], 10);
                if (line > 0) {
                    ranges.push({ start: line, end: line, type: 'single' });
                    continue;
                }
            }
        }

        // 如果没有解析到任何有效区间，回退到第1行
        if (ranges.length === 0) {
            logDebug('行号解析', '无法匹配行号格式，使用第1行作为回退', { lineSpan });
            return { position: 1, hasMultipleRanges: false, ranges: [] };
        }

        // 验证所有区间是否合理
        const validRanges = ranges.filter(range => {
            // 检查行号是否过大
            if (range.start > 100000 || range.end > 100000) {
                logDebug('行号解析', '跳过过大的行号范围', {
                    range,
                    reason: '行号过大'
                });
                return false;
            }

            // 检查范围是否过大
            const rangeSize = range.end - range.start + 1;
            if (range.type === 'range' && rangeSize > 100) {
                logDebug('行号解析', '跳过过大的行号范围', {
                    range,
                    rangeSize,
                    reason: '范围过大'
                });
                return false;
            }

            return true;
        });

        if (validRanges.length === 0) {
            logDebug('行号解析', '所有区间都无效，使用第1行作为回退', {
                originalRanges: ranges
            });
            return { position: 1, hasMultipleRanges: false, ranges: [] };
        }

        // 选择主要提交位置：第一个有效区间的开始行或中间行
        const primaryRange = validRanges[0];
        let primaryPosition = primaryRange.start;

        // 如果是范围且范围较大，选择中间位置
        if (primaryRange.type === 'range' && (primaryRange.end - primaryRange.start + 1) > 10) {
            primaryPosition = Math.floor((primaryRange.start + primaryRange.end) / 2);
            primaryPosition = Math.min(primaryPosition, 1000); // 限制最大行号
        }

        const hasMultipleRanges = validRanges.length > 1;

        logDebug('行号解析', '行号解析完成', {
            original: lineSpan,
            position: primaryPosition,
            hasMultipleRanges,
            totalRanges: validRanges.length,
            ranges: validRanges
        });

        return {
            position: primaryPosition,
            hasMultipleRanges,
            ranges: validRanges
        };
    }

    // 格式化行区间显示，支持多区间
    function formatLineSpanDisplay(lineSpan) {
        if (!lineSpan || lineSpan === '未知') return lineSpan;

        // 检查是否包含多个区间
        const ranges = lineSpan.split(',');
        if (ranges.length > 1) {
            // 多个区间，添加提示标记
            const formattedRanges = ranges.map(range => {
                const trimmed = range.trim();
                // 为不完整的区间格式补充L
                if (trimmed.match(/^\d+$/)) {
                    return `L${trimmed}`;
                }
                return trimmed;
            });
            return formattedRanges.join(', ') + ' <span style="color: #dc3545; font-weight: bold;">(多处)</span>';
        }

        // 单个区间，确保格式正确
        const trimmed = ranges[0].trim();
        if (trimmed.match(/^\d+$/)) {
            return `L${trimmed}`;
        }
        return trimmed;
    }

    
    function buildCommentBody(data, parsedPosition = null) {
        const categoryIcon = getCategoryIcon(data.category);
        const categoryText = data.category || '未分类';

        let body = `🤖 **AI 代码检视意见**\n\n`;
        body += `**类别**: ${categoryIcon} ${categoryText}\n`;
        body += `**严重级别**: ${data.severity || '未知'}\n`;
        body += `**文件**: \`${data.file}\`\n`;

        // 处理行号显示
        if (parsedPosition && parsedPosition.hasMultipleRanges && parsedPosition.ranges.length > 1) {
            // 多个区间的情况
            const allRanges = parsedPosition.ranges.map(range => {
                if (range.type === 'single') {
                    return `L${range.start}`;
                } else {
                    return `L${range.start}-L${range.end}`;
                }
            }).join(', ');

            body += `**行号**: ${allRanges} (多处相同问题)\n\n`;
            body += `**📍 多个位置**: 此问题在 ${parsedPosition.ranges.length} 个位置出现，以上列出了所有相关行号。\n\n`;
        } else {
            // 单个区间的情况
            body += `**行号**: ${data.lineSpan}\n\n`;
        }

        body += `**问题描述**:\n${data.issue}\n\n`;
        body += `**修复建议**:\n${data.suggestion}\n\n`;
        body += `---\n*此评论由 OpenHarmony Insight Review Lite 自动生成*`;

        return body;
    }

    function submitPRComment(repoInfo, token, filePath, position, body, retryCount = 0) {
        return new Promise((resolve, reject) => {
            const apiUrl = `https://api.gitcode.com/api/v5/repos/${repoInfo.owner}/${repoInfo.repo}/pulls/${repoInfo.prId}/comments`;

            const requestData = {
                body: body,
                path: filePath,
                position: position
            };

            // 详细的调试日志
            logDebug('API提交', '开始提交PR评论', {
                apiUrl,
                filePath,
                position,
                bodyLength: body.length,
                retryCount
            });

            // 验证position是否有效
            if (!position || position < 1) {
                const error = new Error(`无效的position值: ${position}`);
                logDebug('API提交', 'position验证失败', { position, filePath });
                reject(error);
                return;
            }

            GM_xmlhttpRequest({
                method: 'POST',
                url: apiUrl,
                headers: {
                    'Content-Type': 'application/json',
                    'Accept': 'application/json',
                    'Authorization': `Bearer ${token}`
                },
                data: JSON.stringify(requestData),
                onload: function(response) {
                    logDebug('API响应', '收到API响应', {
                        status: response.status,
                        statusText: response.statusText,
                        responseText: response.responseText.slice(0, 500) + (response.responseText.length > 500 ? '...' : '')
                    });

                    if (response.status >= 200 && response.status < 300) {
                        try {
                            const result = JSON.parse(response.responseText);
                            logDebug('API提交', 'PR评论提交成功', result);
                            resolve(result);
                        } catch (parseError) {
                            const error = new Error(`响应解析失败: ${parseError.message}`);
                            logDebug('API错误', '响应解析失败', { responseText: response.responseText });
                            reject(error);
                        }
                    } else {
                        let errorMsg = `HTTP ${response.status}: ${response.statusText}`;
                        let errorData = null;

                        try {
                            errorData = JSON.parse(response.responseText);
                            errorMsg = errorData.message || errorMsg;
                        } catch (e) {
                            logDebug('API错误', '无法解析错误响应', { responseText: response.responseText });
                        }

                        const error = new Error(errorMsg);

                        // 详细记录400错误的信息
                        if (response.status === 400) {
                            logDebug('400错误', 'API返回400错误', {
                                position,
                                filePath,
                                requestBody: requestData,
                                errorData,
                                fullResponse: response.responseText
                            });

                            // 尝试智能修复position
                            if (retryCount < 2) {
                                logDebug('自动修复', `尝试智能修复position，第${retryCount + 1}次重试`);

                                // 尝试一些常见的修复策略
                                const possiblePositions = [
                                    Math.max(1, position - 1),  // 尝试前一行
                                    Math.max(1, position + 1),  // 尝试后一行
                                    Math.max(1, Math.floor(position / 2)),  // 尝试中间位置
                                    1  // 尝试第一行
                                ];

                                const newPosition = possiblePositions[retryCount];

                                setTimeout(() => {
                                    submitPRComment(repoInfo, token, filePath, newPosition, body, retryCount + 1)
                                        .then(resolve)
                                        .catch(reject);
                                }, 1000 * (retryCount + 1)); // 递增延迟

                                return;
                            }
                        }

                        reject(error);
                    }
                },
                onerror: function(err) {
                    const error = new Error(`网络请求失败: ${err}`);
                    logDebug('网络错误', '网络请求失败', { error: err.toString() });

                    // 网络错误重试
                    if (retryCount < 3) {
                        logDebug('自动重试', `网络错误重试，第${retryCount + 1}次`);
                        setTimeout(() => {
                            submitPRComment(repoInfo, token, filePath, position, body, retryCount + 1)
                                .then(resolve)
                                .catch(reject);
                        }, 2000 * (retryCount + 1));
                        return;
                    }

                    reject(error);
                }
            });
        });
    }

    function showResultModal() {
        document.getElementById('llm-result-modal-overlay')?.remove();
        const modalHTML = `
            <div id="llm-result-modal-overlay" class="llm-modal-overlay">
                <div class="llm-modal-content">
                    <div class="llm-modal-header">
                        <h3>🤖 OpenHarmony Insight Review Lite</h3>
                        <div>
                            <button id="llm-refresh-btn" title="重新审核" style="cursor: pointer; border: none; background: none; font-size: 18px;">🔄</button>
                            <button id="llm-close-btn" title="关闭" style="cursor: pointer; border: none; background: none; font-size: 18px; margin-left: 8px;">❌</button>
                        </div>
                    </div>
                    <div class="llm-modal-body"></div>
                </div>
            </div>`;
        document.body.insertAdjacentHTML('beforeend', modalHTML);
        const overlay = document.getElementById('llm-result-modal-overlay');
        const body = overlay.querySelector('.llm-modal-body');
        const closeModal = () => overlay.remove();
        overlay.querySelector('#llm-close-btn').onclick = closeModal;
        overlay.querySelector('#llm-refresh-btn').onclick = runAnalysis;
        overlay.onclick = (e) => { if (e.target === overlay) closeModal(); };
        return { overlay, body };
    }

    function updateProgress(modalBody, statusText) {
        const { primary } = getModalSections(modalBody);
        primary.innerHTML = `<div class="llm-progress-view"><div class="llm-spinner"></div><p>${statusText}</p></div>`;
    }

    function renderContent(modalBody, jsonData) {
        const { primary } = getModalSections(modalBody);
        const getAssessmentColor = (assessment) => {
            if (!assessment) return '#6c757d';
            assessment = assessment.toLowerCase();
            if (assessment.includes('sufficient')) return '#28a745';
            if (assessment.includes('improvement')) return '#ffc107';
            if (assessment.includes('insufficient')) return '#dc3545';
            return '#6c757d';
        };
        const getRatingIcon = (rating) => {
            if (!rating) return '';
            rating = rating.toLowerCase();
            if (rating.includes('优秀')) return '✅';
            if (rating.includes('良好')) return '✔️';
            if (rating.includes('待改进')) return '⚠️';
            return '';
        };
        let feedbackHtml = '';
        if (jsonData.detailed_feedback && jsonData.detailed_feedback.length > 0) {
            jsonData.detailed_feedback.forEach(item => {
                feedbackHtml += `
                    <div class="llm-feedback-card">
                        <div class="llm-feedback-card-header">
                            ${getRatingIcon(item.rating)} <strong>${item.aspect || '未知方面'}</strong>
                            <span>${item.rating || '无评级'}</span>
                        </div>
                        <p class="llm-feedback-card-comment">${item.comment || '无评语'}</p>
                    </div>`;
            });
        }
        let missingInfoHtml = '';
        if (jsonData.missing_info && jsonData.missing_info.length > 0) {
            missingInfoHtml = `
                <div class="llm-missing-info-card">
                    <strong>缺失的关键信息:</strong>
                    <ul>${jsonData.missing_info.map(item => `<li>${item}</li>`).join('')}</ul>
                </div>`;
        }
        primary.innerHTML = `
            <div class="llm-summary-card">
                <p style="margin: 0 0 10px 0;">
                    <span class="llm-assessment" style="background-color: ${getAssessmentColor(jsonData.overall_assessment)};">
                        ${jsonData.overall_assessment || '未知'}
                    </span>
                </p>
                <p style="margin: 0;"><strong>摘要:</strong> ${jsonData.summary_comment || '无'}</p>
            </div>
            <div>${feedbackHtml}</div>
            ${missingInfoHtml}
        `;
    }

    function getStatusLabel(status) {
        const mapping = {
            added: '新增',
            deleted: '删除',
            modified: '修改',
            renamed: '重命名'
        };
        return mapping[status] || '未知';
    }

    async function runSecondaryAnalysis(modalBody, config, repoInfo, context) {
        const { secondary } = getModalSections(modalBody);
        if (!secondary) return;

        logDebug('深度审查', '开始分离式深度审查流程', {
            secondaryReviewEnabled: config?.secondaryReviewEnabled,
            hasSecondaryBaseUrl: !!config?.secondaryBaseUrl,
            hasSecondaryApiKey: !!config?.secondaryApiKey,
            hasSecondaryModel: !!config?.secondaryModel,
            hasGitcodeToken: !!config?.gitcodeToken,
            repoInfo,
            contextKeys: context ? Object.keys(context) : null,
            hasCachedResult: !!lastSecondaryFragment
        });

        // 如果有缓存的结果，直接使用，不重新分析
        if (lastSecondaryFragment) {
            logDebug('深度审查', '使用缓存的深度审查结果');
            secondary.innerHTML = '';
            secondary.appendChild(lastSecondaryFragment.cloneNode(true));
            return;
        }

        setSecondaryProgress(secondary, '正在解析 PR diff …');

        try {
            // 并行获取 diff 和文件信息
            setSecondaryProgress(secondary, '正在获取 PR 变更信息...');
            const [diffText, filesData] = await Promise.all([
                fetchPRDiff(repoInfo),
                fetchFilesInfo(repoInfo, config.gitcodeToken)
            ]);

            const fileSummaries = analyzeDiff(diffText);
            if (!fileSummaries.length) {
                secondary.innerHTML = `
                    <div class="llm-summary-card">
                        <p style="margin: 0;"><strong>未在 diff 中检测到文件变更。</strong></p>
                    </div>
                `;
                return;
            }

            const secondaryUrl = config.secondaryBaseUrl || config.baseUrl;
            const secondaryModelName = config.secondaryModel || config.model;

            const groupedEntries = groupDiffEntries(fileSummaries);
            const totals = fileSummaries.reduce((acc, file) => {
                acc.additions += file.additions;
                acc.deletions += file.deletions;
                acc.count += 1;
                acc.statusCounts[file.status] = (acc.statusCounts[file.status] || 0) + 1;
                return acc;
            }, { additions: 0, deletions: 0, count: 0, statusCounts: {} });

            const diffOverview = `总文件数: ${totals.count}, 新增行: ${totals.additions}, 删除行: ${totals.deletions}, 状态分布: ${summarizeStatusCounts(totals.statusCounts) || '无'}`;
            const diffGroupsInfo = groupedEntries.map((group, index) => {
                const statusSummary = summarizeStatusCounts(group.statusCounts) || '无';
                const languageLabel = group.languages.length > 0 ? group.languages.join('，') : '未知';
                const filesText = group.files.map(file => {
                    const snippet = buildFileSnippet(file, 80);
                    const snippetBlock = snippet ? `\n${snippet}` : '\n    (无新增代码片段)';
                    return `  - [${getStatusLabel(file.status)}] ${file.path} (+${file.additions} / -${file.deletions})${snippetBlock}`;
                }).join('\n');
                return `组 ${index + 1}: ${group.title}\n语言: ${languageLabel}\n文件数: ${group.files.length}\n状态分布: ${statusSummary}\n新增行: ${group.additions}, 删除行: ${group.deletions}\n${filesText}`;
            }).join('\n\n');

            // 获取完整文件内容
            setSecondaryProgress(secondary, '正在获取文件的完整内容用于深度分析...');
            let completeFilesContent = '';
            try {
                // 先检查是否有必要的文件信息
                if (!filesData || !filesData.diff_refs) {
                    throw new Error('缺少必要的文件引用信息 (diff_refs)');
                }

                if (!filesData.diffs || filesData.diffs.length === 0) {
                    throw new Error('没有找到文件差异信息 (diffs)');
                }

                // 添加详细的文件类型诊断
                logDebug('深度审查', '诊断文件类型', {
                    totalDiffs: filesData.diffs.length,
                    fileTypes: filesData.diffs.map(diff => ({
                        path: diff.statistic?.new_path || diff.statistic?.old_path || 'unknown',
                        type: diff.statistic?.type,
                        fullStatistic: diff.statistic
                    }))
                });

                const modifiedFiles = filesData.diffs.filter(diff => {
                    const type = diff.statistic?.type;
                    return type === 'modified' || type === 'added' || type === 'deleted';
                });

                logDebug('深度审查', '文件筛选结果', {
                    totalDiffs: filesData.diffs.length,
                    modifiedFiles: modifiedFiles.length,
                    filteredFiles: modifiedFiles.map(diff => ({
                        path: diff.statistic?.new_path || diff.statistic?.old_path || 'unknown',
                        type: diff.statistic?.type
                    }))
                });

                if (modifiedFiles.length === 0) {
                    // 如果筛选后没有文件，说明文件类型不匹配，但我们仍然要处理所有文件
                    logDebug('深度审查', '文件类型不匹配，处理所有文件', {
                        availableTypes: [...new Set(filesData.diffs.map(diff => diff.statistic?.type).filter(Boolean))]
                    });
                    modifiedFiles.push(...filesData.diffs); // 包含所有文件
                }

                if (modifiedFiles.length === 0) {
                    throw new Error('没有找到需要分析的修改文件');
                }

                logDebug('深度审查', '开始获取完整文件内容', {
                    totalDiffs: filesData.diffs.length,
                    modifiedFiles: modifiedFiles.length,
                    hasBaseSha: !!filesData.diff_refs.base_sha,
                    hasHeadSha: !!filesData.diff_refs.head_sha,
                    baseSha: filesData.diff_refs.base_sha?.slice(0, 8) + '...',
                    headSha: filesData.diff_refs.head_sha?.slice(0, 8) + '...'
                });

                const { baseFiles, headFiles } = await fetchModifiedFilesContent(repoInfo, filesData, config.gitcodeToken, fileSummaries);
                completeFilesContent = formatCompleteFilesContent(baseFiles, headFiles, 3, 3000);
                logDebug('深度审查', '获取到完整文件内容', {
                    baseFilesCount: baseFiles.length,
                    headFilesCount: headFiles.length,
                    contentLength: completeFilesContent.length
                });
            } catch (fileError) {
                logDebug('深度审查', '获取完整文件内容失败，将仅使用diff进行分析', {
                    error: fileError.message,
                    stack: fileError.stack,
                    filesData: filesData ? {
                        hasDiffRefs: !!filesData.diff_refs,
                        hasDiffs: !!filesData.diffs,
                        diffsCount: filesData.diffs?.length || 0
                    } : null
                });
                completeFilesContent = `--- 完整文件对比 ---
❌ 获取完整文件内容失败: ${fileError.message}

📋 详细诊断信息:
${filesData ? `
- 文件差异信息: ${filesData.diffs ? filesData.diffs.length : 0} 个文件
- 引用信息: ${filesData.diff_refs ? '已获取' : '缺失'}
- Base SHA: ${filesData.diff_refs?.base_sha?.slice(0, 8) || '无'}...
- Head SHA: ${filesData.diff_refs?.head_sha?.slice(0, 8) || '无'}...` : 'filesData 为空'}

⚠️ 将仅基于diff片段进行分析，建议检查 GitCode Access Token 权限或网络连接

`;
            }

            secondary.innerHTML = '';

            const overviewCard = document.createElement('div');
            overviewCard.className = 'llm-summary-card';
            const overviewTitle = document.createElement('p');
            overviewTitle.style.margin = '0';
            const overviewStrong = document.createElement('strong');
            overviewStrong.textContent = '深度审查 (diff 概览)';
            overviewTitle.appendChild(overviewStrong);
            const overviewMeta = document.createElement('p');
            overviewMeta.style.margin = '0';
            overviewMeta.style.fontSize = '12px';
            overviewMeta.style.color = '#666';
            overviewMeta.textContent = `目标模型: ${secondaryModelName} @ ${secondaryUrl}`;
            const overviewStats = document.createElement('p');
            overviewStats.style.margin = '10px 0 0 0';
            overviewStats.textContent = diffOverview;
            overviewCard.appendChild(overviewTitle);
            overviewCard.appendChild(overviewMeta);
            overviewCard.appendChild(overviewStats);
            secondary.appendChild(overviewCard);

            // 显示分离的语言组
            groupedEntries.forEach((group, groupIdx) => {
                const card = document.createElement('div');
                card.className = 'llm-summary-card';
                card.style.overflowX = 'auto';
                card.style.border = group.reviewType === 'cpp' ? '2px solid #dc3545' : '2px solid #0366d6';

                const title = document.createElement('h4');
                title.style.margin = '0 0 6px 0';
                title.style.fontSize = '15px';
                title.style.color = group.reviewType === 'cpp' ? '#dc3545' : '#0366d6';
                title.innerHTML = `${group.reviewType === 'cpp' ? '⚙️' : '📝'} 组 ${groupIdx + 1}: ${group.title}`;
                card.appendChild(title);

                const info = document.createElement('p');
                info.style.margin = '0 0 4px 0';
                info.style.color = '#6c757d';
                info.textContent = `语言: ${group.languages.join('，') || '未知'} | 文件: ${group.files.length} | 新增: ${group.additions} | 删除: ${group.deletions}`;
                card.appendChild(info);

                const status = document.createElement('p');
                status.style.margin = '0 0 6px 0';
                status.style.color = '#6c757d';
                status.textContent = `状态: ${summarizeStatusCounts(group.statusCounts) || '无'}`;
                card.appendChild(status);

                const tableContainer = document.createElement('div');
                tableContainer.className = 'llm-table-container';
                tableContainer.style.maxHeight = '300px';

                const table = document.createElement('table');
                table.style.width = '100%';
                table.style.borderCollapse = 'collapse';
                table.style.fontSize = '13px';
                const thead = document.createElement('thead');
                const headRow = document.createElement('tr');
                headRow.style.textAlign = 'left';
                headRow.style.borderBottom = '1px solid #e9ecef';
                ['文件', '状态', '语言', '新增', '删除'].forEach(text => {
                    const th = document.createElement('th');
                    th.style.padding = '6px 8px';
                    th.style.fontWeight = '600';
                    th.style.backgroundColor = '#f8f9fa';
                    th.textContent = text;
                    headRow.appendChild(th);
                });
                thead.appendChild(headRow);
                table.appendChild(thead);

                const tbody = document.createElement('tbody');
                group.files.forEach(file => {
                    const row = document.createElement('tr');
                    const fileCell = document.createElement('td');
                    fileCell.style.padding = '6px 8px';
                    fileCell.style.wordBreak = 'break-all';
                    const code = document.createElement('code');
                    code.style.fontSize = '12px';
                    code.textContent = file.path;
                    fileCell.appendChild(code);
                    row.appendChild(fileCell);

                    const statusCell = document.createElement('td');
                    statusCell.style.padding = '6px 8px';
                    statusCell.textContent = getStatusLabel(file.status);
                    row.appendChild(statusCell);

                    const langCell = document.createElement('td');
                    langCell.style.padding = '6px 8px';
                    langCell.textContent = file.language;
                    row.appendChild(langCell);

                    const addCell = document.createElement('td');
                    addCell.style.padding = '6px 8px';
                    addCell.style.color = '#28a745';
                    addCell.style.fontWeight = '600';
                    addCell.textContent = `+${file.additions}`;
                    row.appendChild(addCell);

                    const delCell = document.createElement('td');
                    delCell.style.padding = '6px 8px';
                    delCell.style.color = '#dc3545';
                    delCell.style.fontWeight = '600';
                    delCell.textContent = `-${file.deletions}`;
                    row.appendChild(delCell);

                    tbody.appendChild(row);

                    const snippet = buildFileSnippet(file, 80);
                    if (snippet) {
                        const snippetRow = document.createElement('tr');
                        const snippetCell = document.createElement('td');
                        snippetCell.colSpan = 5;
                        snippetCell.style.padding = '0 4px 8px 4px';
                        const details = document.createElement('details');
                        details.style.marginTop = '8px';
                        const summary = document.createElement('summary');
                        summary.style.cursor = 'pointer';
                        summary.style.fontSize = '13px';
                        summary.textContent = `${file.path} 代码摘录`;
                        const pre = document.createElement('pre');
                        pre.className = 'llm-code-snippet';
                        pre.textContent = snippet;
                        details.appendChild(summary);
                        details.appendChild(pre);
                        snippetCell.appendChild(details);
                        snippetRow.appendChild(snippetCell);
                        tbody.appendChild(snippetRow);
                    }
                });

                table.appendChild(tbody);
                tableContainer.appendChild(table);
                card.appendChild(tableContainer);
                secondary.appendChild(card);
            });

            const llmResultContainer = document.createElement('div');
            llmResultContainer.id = 'secondary-llm-result';
            llmResultContainer.className = 'llm-summary-card';
            llmResultContainer.style.marginTop = '20px';
            secondary.appendChild(llmResultContainer);

            // 根据文件分组选择合适的模板
            const hasCppSystemFiles = groupedEntries.some(g => g.reviewType === 'cpp');
            const promptTemplate = hasCppSystemFiles ? CPP_SYSTEM_TEMPLATE : DEFAULT_SECONDARY_TEMPLATE;
            const prompt = promptTemplate
                .replace('{pr_title}', context?.prTitle || '(未知标题)')
                .replace('{pr_description}', context?.prDescription || '(无描述)')
                .replace('{diff_overview}', diffOverview)
                .replace('{diff_groups_info}', diffGroupsInfo)
                .replace('{complete_files_content}', completeFilesContent)
                .replace('{issues_info}', context?.issuesInfoText || '（无关联 Issue 信息）');

            setSecondaryProgress(llmResultContainer, '正在准备深度审查请求...');

            GM_xmlhttpRequest({
                method: 'POST',
                url: secondaryUrl,
                headers: {
                    'Content-Type': 'application/json',
                    'Authorization': `Bearer ${config.secondaryApiKey || config.apiKey}`
                },
                data: JSON.stringify({
                    model: secondaryModelName,
                    messages: [{ role: 'user', content: prompt }],
                    stream: false
                }),
                onload: function(response) {
                    if (response.status < 200 || response.status >= 300) {
                        llmResultContainer.innerHTML = `<div class="llm-progress-view"><h4 style=\"color: #dc3545;\">⚠️ 深度审查请求失败</h4><p>HTTP ${response.status}</p><pre style=\"white-space: pre-wrap; background-color: #eee; padding: 10px; border-radius: 5px; text-align: left;\">${response.responseText}</pre></div>`;
                    return;
                }
                try {
                    const data = JSON.parse(response.responseText);
                    const reviewContent = data.choices[0].message.content;
                    const cleanMessage = reviewContent.replace(/```json/g, '').replace(/```/g, '').trim();
                    const jsonData = JSON.parse(cleanMessage);
                    renderSecondaryLLMResult(llmResultContainer, jsonData);
                } catch (error) {
                    console.error('Secondary LLM parse error:', error);
                    llmResultContainer.innerHTML = `<div class="llm-progress-view"><h4 style=\"color: #dc3545;\">⚠️ 深度审查结果解析失败</h4><p>${error}</p><pre style=\"white-space: pre-wrap; background-color: #eee; padding: 10px; border-radius: 5px; text-align: left;\">${response.responseText}</pre></div>`;
                }
            },
            onerror: function(err) {
                llmResultContainer.innerHTML = `<div class="llm-progress-view"><h4 style=\"color: #dc3545;\">⚠️ 深度审查请求失败</h4><p>${JSON.stringify(err)}</p></div>`;
            }
        });

        } catch (error) {
            secondary.innerHTML = `
                <div class="llm-progress-view">
                    <h4 style="color: #dc3545;">⚠️ 解析 PR diff 失败</h4>
                    <p>${error}</p>
                    <p>可以稍后重试，或检查 PR 是否对外提供 diff 访问。</p>
                </div>
            `;
        }
    }

    async function runAnalysis() {
        const { body: modalBody } = showResultModal();
        updateProgress(modalBody, '正在初始化...');
        const config = await getConfig();
        const { primary, secondary } = getModalSections(modalBody);

        logDebug('主分析', '开始新的分析流程', {
            hasCachedSecondaryResult: !!lastSecondaryFragment,
            willClearCache: false
        });

        // 不要清除深度审查缓存，让它保持有效
        // lastSecondaryFragment = null; // 注释掉这行

        if (!config) {
            primary.innerHTML = `
                <div class="llm-progress-view">
                    <h4 style="color: #dc3545;">⚠️ 配置不完整</h4>
                    <p>请通过油猴菜单补全 LLM 与 GitCode 配置信息。</p>
                    <button id="open-config-btn" style="margin-top: 10px; padding: 8px 12px; cursor: pointer; border: 1px solid #ccc; border-radius: 5px; background-color: #0366d6; color: white;">立即配置</button>
                </div>`;
            primary.querySelector('#open-config-btn').onclick = openConfigModal;
            secondary.innerHTML = '';
            return;
        }

        const repoInfo = extractRepoInfo();
        if (!repoInfo) {
            primary.innerHTML = `
                <div class="llm-progress-view">
                    <h4 style="color: #dc3545;">⚠️ 解析仓库信息失败</h4>
                    <p>无法从当前页面的 URL 中推断仓库信息。</p>
                </div>`;
            secondary.innerHTML = '';
            return;
        }

        try {
            updateProgress(modalBody, '正在从 GitCode API 获取 PR 信息...');
            const [prDetails, filesData] = await Promise.all([
                fetchPRDetails(repoInfo, config.gitcodeToken),
                fetchFilesInfo(repoInfo, config.gitcodeToken)
            ]);

            // 检测 PR 是否发生变更
            const prChanged = hasPrChanged(prDetails);

            // 如果 PR 发生变更，清除缓存
            if (prChanged) {
                logDebug('主分析', 'PR 已变更，清除所有缓存');
                lastAnalysisResult = null;
                lastAnalysisContext = null;
                lastSecondaryFragment = null;
            }

            const prTitle = prDetails.title;
            const prDescription = prDetails.body || '（无描述）';
            const fileList = filesData.diffs.map(d => `- ${d.statistic.type}: ${d.statistic.path} (+${d.added_lines} / -${d.remove_lines})`).join('\n');
            const filesInfoText = `总体变更: ${filesData.count} 个文件, +${filesData.added_lines} 行, -${filesData.remove_lines} 行.\n\n文件列表:\n${fileList}`;

            // 更新 PR 分析状态
            updatePrAnalysisState(prDetails);
            let issuesInfoText = '未找到或无法解析关联的 Issue。';
            const issueUrlRegex = /\/([^\/]+)\/([^\/]+)\/issues\/(\d+)/g;
            const issuesToFetch = [...prDescription.matchAll(issueUrlRegex)].map(match => ({ owner: match[1], repo: match[2], number: match[3] }));
            if (issuesToFetch.length > 0) {
                updateProgress(modalBody, `正在获取 ${issuesToFetch.length} 个关联 Issue 的详细信息...`);
                const issuePromises = issuesToFetch.map(issue => fetchIssueDetails(issue, config.gitcodeToken));
                const detailedIssues = await Promise.all(issuePromises);
                issuesInfoText = detailedIssues.map(issue => `--------------------\nIssue #${issue.number}: ${issue.title}\nURL: ${issue.html_url}\n\n[Body]:\n${issue.body}`).join('\n\n');
            }

            lastAnalysisContext = { repoInfo, prTitle, prDescription, issuesInfoText };
            if (config.secondaryReviewEnabled) {
                runSecondaryAnalysis(modalBody, config, repoInfo, lastAnalysisContext);
            } else {
                secondary.innerHTML = '';
            }

            updateProgress(modalBody, '已获取所有上下文，正在发送给 LLM 进行分析...');
            const prompt = config.template
                .replace('{pr_title}', prTitle)
                .replace('{pr_description}', prDescription)
                .replace('{files_info}', filesInfoText)
                .replace('{issues_info}', issuesInfoText);

            GM_xmlhttpRequest({
                method: 'POST',
                url: config.baseUrl,
                headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${config.apiKey}` },
                data: JSON.stringify({ model: config.model, messages: [{ role: 'user', content: prompt }], stream: false }),
                onload: function(response) {
                    try {
                        const data = JSON.parse(response.responseText);
                        const reviewContent = data.choices[0].message.content;
                        const cleanMessage = reviewContent.replace(/```json/g, '').replace(/```/g, '').trim();
                        const jsonData = JSON.parse(cleanMessage);
                        lastAnalysisResult = jsonData;
                        renderContent(modalBody, jsonData);
                    } catch (e) {
                        console.error('LLM Reviewer Error:', e);
                        const { primary } = getModalSections(modalBody);
                        primary.innerHTML = `<div class="llm-progress-view"><h4 style="color: #dc3545;">⚠️ 模型返回格式错误或解析失败</h4><p>这可能是因为模型没有严格遵守JSON输出格式。请检查模型的输出。</p><pre style="white-space: pre-wrap; background-color: #eee; padding: 10px; border-radius: 5px; text-align: left;">${response.responseText}</pre></div>`;
                    }
                },
                onerror: function(error) {
                    const { primary } = getModalSections(modalBody);
                    primary.innerHTML = `<div class="llm-progress-view"><h4 style="color: #dc3545;">⚠️ 请求 LLM 失败</h4><p>${JSON.stringify(error)}</p></div>`;
                }
            });
        } catch (error) {
            const { primary, secondary } = getModalSections(modalBody);
            primary.innerHTML = `<div class="llm-progress-view"><h4 style="color: #dc3545;">⚠️ 操作失败</h4><p>${error}</p><p>请检查您的 GitCode Access Token 是否正确、有权限，并刷新页面重试。</p></div>`;
            secondary.innerHTML = '';
        }
    }

    // --- 配置模态框 ---
    function openConfigModal() {
        document.getElementById('config-modal-overlay')?.remove();
        const modalHTML = `
            <div id="config-modal-overlay" class="llm-modal-overlay">
                <div class="llm-modal-content" style="width: 600px;">
                    <div class="llm-modal-header"><h2 style="margin:0; font-size: 18px;">⚙️ 配置审核助手</h2></div>
                    <div class="llm-modal-body" style="display: flex; flex-direction: column; gap: 15px; max-height: 70vh; overflow-y: auto;">
                        <fieldset style="border: 1px solid #ccc; padding: 10px; border-radius: 5px;">
                            <legend>主要审核配置</legend>
                            <label>LLM Base URL:</label>
                            <input type="text" id="config-llm-base-url" style="width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; margin-top: 5px; margin-bottom: 10px;">
                            <label>LLM API Key:</label>
                            <input type="password" id="config-llm-api-key" style="width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; margin-top: 5px; margin-bottom: 10px;">
                            <label>LLM 模型名称:</label>
                            <input type="text" id="config-llm-model" style="width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; margin-top: 5px; margin-bottom: 10px;">
                        </fieldset>
                        <fieldset style="border: 1px solid #ccc; padding: 10px; border-radius: 5px;">
                            <legend>通用配置</legend>
                            <label>GitCode Access Token:</label>
                            <input type="password" id="config-gitcode-token" style="width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; margin-top: 5px; margin-bottom: 10px;">
                            <label>Prompt 模板:</label>
                            <textarea id="config-prompt-template" style="width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; min-height: 150px; resize: vertical; margin-top: 5px;"></textarea>
                        </fieldset>
                        <hr style="border: none; border-top: 1px solid #eee; margin: 10px 0;" />
                        <div>
                            <label style="display: flex; align-items: center; gap: 8px; cursor: pointer;">
                                <input type="checkbox" id="config-enable-secondary-review">
                                <strong>启用深度审查 (专业化代码分析)</strong>
                            </label>
                        </div>
                        <fieldset id="secondary-config-fieldset" style="border: 1px solid #ccc; padding: 10px; border-radius: 5px; margin-top: 5px;">
                            <legend>深度审查配置 (使用专业化模型)</legend>
                            <label>Secondary LLM Base URL:</label>
                            <input type="text" id="config-secondary-llm-base-url" placeholder="https://open.bigmodel.cn/api/paas/v4/chat/completions" style="width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; margin-top: 5px; margin-bottom: 10px;">
                            <label>Secondary LLM API Key:</label>
                            <input type="password" id="config-secondary-llm-api-key" placeholder="默认使用主要配置的API Key" style="width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; margin-top: 5px; margin-bottom: 10px;">
                            <label>Secondary LLM 模型名称:</label>
                            <input type="text" id="config-secondary-llm-model" placeholder="GLM-4.6" style="width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; margin-top: 5px; margin-bottom: 10px;">
                            <p style="margin: 10px 0; color: #6c757d; font-size: 12px;">
                                💡 <strong>默认已配置专业化模型：</strong>GLM-4.6<br>
                                🎯 <strong>推荐用途：</strong>深度代码审查、复杂逻辑分析、架构评估<br>
                                ⚡ <strong>性能特点：</strong>最新一代模型，比GLM-4.5更强的代码分析能力，专业级安全性和可靠性审查
                            </p>
                            <p style="margin: 10px 0; color: #28a745; font-size: 12px; border-left: 3px solid #28a745; padding-left: 8px;">
                                ✨ 深度审查使用内置的专业化代码检视提示词，基于完整文件内容进行深度分析，包含安全、可靠性、架构、质量四个维度的系统评估，输出具体的问题定位和修复建议。
                            </p>
                        </fieldset>
                    </div>
                    <div style="margin-top: 20px; text-align: right; border-top: 1px solid #eee; padding: 15px 20px 12px;">
                        <button id="config-save-btn" style="padding: 8px 15px; border: none; border-radius: 5px; background-color: #0366d6; color: white; cursor: pointer;">保存</button>
                        <button id="config-cancel-btn" style="padding: 8px 15px; border: 1px solid #ccc; border-radius: 5px; background-color: #f5f5f5; color: #333; cursor: pointer; margin-left: 10px;">取消</button>
                    </div>
                </div>
            </div>`;
        document.body.insertAdjacentHTML('beforeend', modalHTML);
        const overlay = document.getElementById('config-modal-overlay');
        const closeModal = () => overlay.remove();
        overlay.querySelector('#config-cancel-btn').onclick = closeModal;
        overlay.onclick = (e) => { if (e.target === overlay) closeModal(); };

        const secondaryReviewSwitch = overlay.querySelector('#config-enable-secondary-review');
        const secondaryConfigFields = overlay.querySelector('#secondary-config-fieldset');

        secondaryReviewSwitch.onchange = () => {
            secondaryConfigFields.style.display = secondaryReviewSwitch.checked ? 'block' : 'none';
        };

        (async () => {
            // Load primary configs
            overlay.querySelector('#config-llm-base-url').value = await GM_getValue('LLM_BASE_URL', DEFAULT_BASE_URL);
            overlay.querySelector('#config-llm-api-key').value = await GM_getValue('API_KEY', '');
            overlay.querySelector('#config-llm-model').value = await GM_getValue('LLM_MODEL', DEFAULT_MODEL);
            overlay.querySelector('#config-gitcode-token').value = await GM_getValue('GITCODE_ACCESS_TOKEN', '');
            overlay.querySelector('#config-prompt-template').value = await GM_getValue('PROMPT_TEMPLATE', DEFAULT_TEMPLATE);

            // Load secondary configs
            overlay.querySelector('#config-secondary-llm-base-url').value = await GM_getValue('SECONDARY_LLM_BASE_URL', DEFAULT_SECONDARY_BASE_URL);
            overlay.querySelector('#config-secondary-llm-api-key').value = await GM_getValue('SECONDARY_API_KEY', '');
            overlay.querySelector('#config-secondary-llm-model').value = await GM_getValue('SECONDARY_LLM_MODEL', DEFAULT_SECONDARY_MODEL);
            secondaryReviewSwitch.checked = await GM_getValue('SECONDARY_REVIEW_ENABLED', false);

            // Trigger initial state
            secondaryReviewSwitch.onchange();
        })();

        overlay.querySelector('#config-save-btn').onclick = async () => {
            // Save primary configs
            await GM_setValue('LLM_BASE_URL', overlay.querySelector('#config-llm-base-url').value);
            await GM_setValue('API_KEY', overlay.querySelector('#config-llm-api-key').value);
            await GM_setValue('LLM_MODEL', overlay.querySelector('#config-llm-model').value);
            await GM_setValue('GITCODE_ACCESS_TOKEN', overlay.querySelector('#config-gitcode-token').value);
            await GM_setValue('PROMPT_TEMPLATE', overlay.querySelector('#config-prompt-template').value);

            // Save secondary configs
            await GM_setValue('SECONDARY_REVIEW_ENABLED', overlay.querySelector('#config-enable-secondary-review').checked);
            await GM_setValue('SECONDARY_LLM_BASE_URL', overlay.querySelector('#config-secondary-llm-base-url').value);
            await GM_setValue('SECONDARY_API_KEY', overlay.querySelector('#config-secondary-llm-api-key').value);
            await GM_setValue('SECONDARY_LLM_MODEL', overlay.querySelector('#config-secondary-llm-model').value);

            // Clean up old secondary template config (no longer needed)
            await GM_deleteValue('SECONDARY_PROMPT_TEMPLATE');

            closeModal();
            alert('配置已保存！');
        };
    }

    // --- 菜单命令注册 ---
    GM_registerMenuCommand('⚙️ 配置审核助手', openConfigModal);
    GM_registerMenuCommand('🐛 调试模式', () => {
        setDebugMode(!debugMode);
        const status = debugMode ? '已启用' : '已禁用';
        alert(`调试模式${status}\n\n${debugMode ? '所有API调用和错误信息将被详细记录。点击调试按钮查看日志。' : '调试日志将不再记录。'}`);
    });
    GM_registerMenuCommand('📋 查看调试日志', () => {
        setDebugMode(true);
        showDebugPanel();
    });
    GM_registerMenuCommand('🔍 诊断配置问题', async () => {
        const config = await getConfig();
        const repoInfo = extractRepoInfoFromPage();

        let diagnosis = `🔍 配置诊断报告
生成时间: ${new Date().toLocaleString()}

=== 基础配置 ===
✅ GitCode Token: ${config?.gitcodeToken ? '已配置' : '❌ 未配置'}
✅ LLM Base URL: ${config?.baseUrl ? '已配置' : '❌ 未配置'}
✅ LLM API Key: ${config?.apiKey ? '已配置' : '❌ 未配置'}
✅ LLM Model: ${config?.model || '❌ 未配置'}

=== 深度审查配置 ===
✅ 深度审查启用: ${config?.secondaryReviewEnabled ? '已启用' : '❌ 未启用'}
✅ 专用LLM URL: ${config?.secondaryBaseUrl === DEFAULT_SECONDARY_BASE_URL ? `默认 (${DEFAULT_SECONDARY_BASE_URL})` : (config?.secondaryBaseUrl ? '已配置' : '使用主要配置')}
✅ 专用LLM Key: ${config?.secondaryApiKey ? '已配置' : '使用主要配置'}
✅ 专用LLM Model: ${config?.secondaryModel === DEFAULT_SECONDARY_MODEL ? `默认 (${DEFAULT_SECONDARY_MODEL})` : (config?.secondaryModel || '使用主要配置')}

=== 网络状态 ===
✅ 在线状态: ${isOnline() ? '在线' : '❌ 离线'}
✅ User Agent: ${navigator.userAgent.slice(0, 50)}...

=== 当前页面信息 ===
✅ 仓库信息: ${repoInfo ? `${repoInfo.owner}/${repoInfo.repo} #${repoInfo.prId}` : '❌ 无法提取'}
✅ 页面类型: ${window.location.href.includes('/pulls/') ? 'PR 页面' : '❌ 非 PR 页面'}

=== 缓存状态 ===
✅ 主分析缓存: ${lastAnalysisResult ? '有缓存' : '无缓存'}
✅ 深度审查缓存: ${lastSecondaryFragment ? '有缓存' : '无缓存'}
✅ 渐进式缓存: ${analysisState.currentStep !== 'idle' ? `活跃 (${analysisState.currentStep})` : '无缓存'}
  - PR 基本信息: ${analysisState.prDetails ? '已缓存' : '未缓存'}
  - 文件信息: ${analysisState.filesData ? '已缓存' : '未缓存'}
  - Issue 信息: ${analysisState.issuesData !== null ? '已缓存' : '未缓存'}
  - LLM 结果: ${analysisState.llmResult ? '已缓存' : '未缓存'}
  - 深度审查结果: ${analysisState.secondaryResult ? '已缓存' : '未缓存'}

=== 诊断结果 ===`;

        if (!config) {
            diagnosis += '\n❌ 配置缺失：无法获取完整配置，请检查基础配置';
        } else if (!config.gitcodeToken || !config.baseUrl || !config.apiKey || !config.model) {
            diagnosis += '\n❌ 基础配置不完整，请完善必要配置项';
        } else if (!config.secondaryReviewEnabled) {
            diagnosis += '\n⚠️ 深度审查功能未启用，不会获取完整文件内容进行深度分析';
            diagnosis += '\n💡 解决方案：请在配置中启用"启用深度审查(专业化代码分析)"选项';
            diagnosis += '\n🎯 推荐配置：默认已配置专业模型 GLM-4.6，最新一代模型具备更强的代码分析能力';
        } else if (!repoInfo) {
            diagnosis += '\n❌ 页面信息提取失败，请确保在 GitCode PR 页面使用';
        } else if (!isOnline()) {
            diagnosis += '\n❌ 网络连接不可用，请检查网络设置';
        } else {
            diagnosis += '\n✅ 配置检查通过，应该可以看到 GitCode API 请求';
            diagnosis += '\n💡 如果仍然看不到 API 请求，请检查：';
            diagnosis += '\n   1. 是否启用了调试模式查看详细日志';
            diagnosis += '\n   2. 浏览器开发者工具 Network 面板是否过滤了某些请求';
            diagnosis += '\n   3. 是否有浏览器扩展阻止了请求';
            diagnosis += '\n   4. GitCode 服务是否正常可用';

            // 检查深度审查配置优化建议
            if (config.secondaryReviewEnabled) {
                if (config.secondaryModel === DEFAULT_SECONDARY_MODEL && !config.secondaryApiKey) {
                    diagnosis += '\n🎯 深度审查优化建议：当前使用主要配置的API Key，建议配置专用API Key以获得更好的并发性能';
                }
                if (config.secondaryBaseUrl === DEFAULT_SECONDARY_BASE_URL && config.secondaryModel === DEFAULT_SECONDARY_MODEL) {
                    diagnosis += '\n✨ 深度审查已使用推荐配置：GLM-4.6 模型，最新一代专业级代码分析能力';
                }
            }
        }

        diagnosis += '\n\n🛠️ 常见问题解决：';
        diagnosis += '\n- 如果遇到 "Failed to fetch" 错误，请检查网络连接';
        diagnosis += '\n- 如果遇到 "Cannot create item with duplicate id"，这是其他扩展的问题，不影响本脚本';
        diagnosis += '\n- 建议禁用其他可能冲突的浏览器扩展进行测试';

        alert(diagnosis);
    });
    GM_registerMenuCommand('🗑️ 清除缓存', () => {
        const result = confirm('确定要清除所有缓存的分析结果吗？\n这将强制重新分析，可能会消耗更多的 API 调用。');
        if (result) {
            lastAnalysisResult = null;
            lastAnalysisContext = null;
            lastSecondaryFragment = null;
            lastAnalyzedPrUrl = null;
            lastAnalyzedPrUpdatedAt = null;
            clearAnalysisCache(); // 清除渐进式缓存
            alert('✅ 所有缓存已清除，下次分析将重新开始。');
        }
    });

    // 测试行号解析功能（仅在开发模式下使用）
    function testLineParsing() {
        console.log('🧪 开始测试行号解析功能...');

        const testCases = [
            { lineSpan: 'L15', expected: 15 },
            { lineSpan: 'L15-L27', expected: 15 },
            { lineSpan: 'L15-L27,L35', expected: 15 },
            { lineSpan: '42', expected: 42 },
            { lineSpan: 'L10-L50', expected: 10 },
            { lineSpan: 'L5-L15', expected: 5 }
        ];

        testCases.forEach((testCase, index) => {
            const result = parseLinePosition(testCase.lineSpan);
            const status = result === testCase.expected ? '✅' : '❌';
            console.log(`${status} 测试 ${index + 1}: ${testCase.lineSpan} -> ${result} (期望: ${testCase.expected})`);
        });

        console.log('🧪 行号解析功能测试完成');
    }

    // 全局错误处理
    window.addEventListener('error', function(event) {
        // 只处理我们脚本相关的错误
        if (event.filename && event.filename.includes('gitcode-review-helper')) {
            logDebug('全局错误捕获', '脚本错误', {
                message: event.message,
                filename: event.filename,
                lineno: event.lineno,
                colno: event.colno,
                error: event.error?.stack
            });
        }
    });

    window.addEventListener('unhandledrejection', function(event) {
        // 只处理我们脚本相关的 Promise 错误
        if (event.reason && typeof event.reason === 'object' && event.reason.stack) {
            const stack = event.reason.stack;
            if (stack.includes('gitcode-review-helper') || stack.includes('runAnalysis') || stack.includes('runSecondaryAnalysis')) {
                logDebug('全局错误捕获', '未处理的 Promise 错误', {
                    reason: event.reason,
                    stack: stack
                });

                // 防止错误在控制台显示
                event.preventDefault();

                // 可以在这里添加用户友好的错误提示
                console.warn('OpenHarmony Insight Review Lite: 捕获到未处理的错误，已记录到调试日志');
            }
        }
    });

    // --- 脚本启动 ---
    const checkExist = setInterval(function() {
        if (document.getElementById('llm-review-fab')) {
            clearInterval(checkExist);
        } else {
            injectStyles();
            addFloatingActionButton();

            // 添加全局事件委托，确保所有动态生成的提交按钮都能正常工作
            setupGlobalEventDelegation();

            // 开发模式下测试行号解析功能
            if (window.location.hostname === 'localhost' || window.location.hostname.includes('test')) {
                setTimeout(testLineParsing, 2000);
            }
        }
    }, 1000);

})();
